Real-time collaboration has become more critical in today’s digital world. Whether for remote work, online gaming, or shared creative projects, interacting and collaborating in real time is a crucial feature of many modern applications.
In this article, we’ll explore PartyKit, a powerful SDK designed to simplify the development of real-time collaborative applications. We will also demonstrate how to use PartyKit to build a real-time code editor with collaborator functionality in React.
Jump ahead:
PartyKit is an SDK designed for building real-time, collaborative applications leveraging WebSockets to make the process easier. It supports several popular collaboration frameworks like Y.js, automerge, and replicache, allowing developers to quickly and easily add real-time collaboration features to existing apps with minimal code.
PartyKit also enables developers to build multiplayer apps and add real-time experiences to existing projects without worrying about the operational complexity and cost of real-time infrastructure. Let’s take a look at some of its key features:
To demonstrate how to leverage PartyKit to create a multiplayer app, let’s build a real-time code editor with collaborator functionality using React and PartyKit.
To get started, install the required dependencies by running the following command:
> yarn add react react-dom partykit partysocket > yarn add -D parcel @types/react @types/react-dom
The core elements of a PartyKit application consist of the server and the client. The server is a JavaScript module that exports an object; the object outlines the server’s behavior, mainly responding to WebSocket events. The PartyKit client establishes a connection with this server and listens to the events.
Here’s the server-side code; it’s a simple script written in TypeScript:
import type { PartyKitServer } from "partykit/server"; export default { async onConnect(ws, room) { const codeEditorValue = (await room.storage.get( "codeEditorValue" )) as string; if (codeEditorValue) ws.send(codeEditorValue); }, onMessage(message, ws, room) { room.storage.put("codeEditorValue", message); room.broadcast(message as string, [ws.id]); }, } satisfies PartyKitServer;
The above code imports the PartyKitServer
type from the partykit/server
module. Then it exports an object that satisfies the PartyKitServer
interface, meaning it contains the methods expected by PartyKit to handle server-side operations.
The exported object contains two methods:
onConnect(ws, room)
: This method is triggered whenever a new client (ws
) connects to a room
. It retrieves the current value of the code editor from the room
storage. If there is a value stored, it sends the value to the newly connected client. This ensures that the new client receives the current state of the code editor when they connectonMessage(message, ws, room)
: This method is triggered whenever a message is received from a client. It updates the room
storage with the new codeEditorValue
received in the message
. Next, it broadcasts this message
to all clients connected to the room
, except the client that sent the message
Now, let’s deploy the application to the cloud using the following command:
npx partykit deploy server/index.ts --name vijit-ail-demo
Be sure to take note of the production URL of the deployed code. You’ll need it later to establish a connection to the server from the frontend code.
Next, let’s proceed to configure the frontend. The following code snippets will facilitate the creation of a basic React application:
<!-- app/index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>PartyKit App</title> </head> <body> <div id="app"></div> <script type="module" src="index.tsx"></script> </body> </html>
// app/index.tsx import { createRoot } from "react-dom/client"; import React from "react"; import { App } from "./App"; const container = document.getElementById("app") as HTMLElement; const root = createRoot(container); root.render(<App />);
Run the following command to install the monaco-editor
and the uuid
package:
yarn add @monaco-editor/react monaco-editor uuid
The monaco-editor
is a robust and feature-rich code editor that powers VS Code. Its integration into our application will provide a sophisticated editing interface, enabling seamless code editing and collaboration.
The uuid
package is necessary for generating unique identifiers. These identifiers will be instrumental in distinguishing between different sessions in our collaborative code editor application.
The following code sets up a simple real-time collaborative code editor using the PartySocket client API and Monaco Editor in a React app. When a user types in the editor, the value is sent to the server and broadcasted to all other connected clients, ensuring that all users see the same content in real time:
import PartySocket from "partysocket"; import React, { useEffect, useState } from "react"; import Editor from "@monaco-editor/react"; import * as uuid from "uuid"; let roomId = window.location.hash?.substring(1); if (!roomId) { roomId = uuid.v4(); window.location.hash = roomId; } const socket = new PartySocket({ host: "vijit-ail-demo.vijit-ail.partykit.dev", room: roomId, }); export function App() { const [editorValue, setEditorValue] = useState(""); const handleChange = (value: string | undefined) => { if (value === undefined) return; socket.send(value); }; const onIncomingMessage = (message: MessageEvent) => { setEditorValue(message.data); }; useEffect(() => { socket.addEventListener("message", onIncomingMessage); return () => socket.removeEventListener("message", onIncomingMessage); }, []); return ( <Editor height="90vh" defaultLanguage="javascript" theme="vs-dark" onChange={handleChange} value={editorValue} /> ); }
In the above code, we extract the roomId
from the URL. If it doesn’t exist, we generate a new roomId
using the uuid
package and update the URL.
Next, we create a new PartySocket
instance, specifying the host
and room
in its configuration. In the App
component, we define a piece of state editorValue
to hold the current value of the editor.
The handleChange
function sends the current value of the editor to the server whenever it changes. The onIncomingMessage
function updates the editorValue
state whenever a new message is received from the server.
In the useEffect
Hook, we set up an event listener for the message
event on the socket
. Finally, we render the Editor
component from @monaco-editor/react
, passing the necessary props including editorValue
, and the handleChange
function as the onChange
callback.
Here’s the final result of the code, a real-time collaboration between two users:
Socket.IO has long been famous for enabling real-time bidirectional event-based communication in applications. It has served as a reliable library that uses WebSockets as a transport protocol with fallbacks on the HTTP long-polling in environments where WebSockets are not supported.
PartyKit, on the other hand, is a relatively new entrant but comes with a host of features that make it an attractive alternative. Here are some key distinctions between PartyKit and Socket.IO:
While Socket.IO remains a powerful and widely-used library for real-time applications, PartyKit offers several unique benefits that make it an attractive alternative, particularly for developers building real-time collaborative applications or those looking for inbuilt edge computing support and developer-friendly tools.
PartyKit is a powerful and user-friendly SDK that simplifies the development of real-time collaborative applications. This article demonstrated how to build a real-time code editor with collaborator functionality using React and PartyKit. We investigated the benefits and improvements that PartyKit offers over Socket.IO. With its ease of use, support for popular collaboration frameworks, and inbuilt developer tools, PartyKit is an excellent choice for developers looking to build real-time, collaborative applications.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to implement one-way and two-way data binding in Vue.js, using v-model and advanced techniques like defineModel for better apps.
Compare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.