DEV Community

Cover image for Build a real-time, collaborative code editor with PartyKit
Matt Angelosanto for LogRocket

Posted on • Originally published at blog.logrocket.com

Build a real-time, collaborative code editor with PartyKit

Written by Vijit Ail✏️

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:

What is PartyKit?

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:

  • Ease of use: Requires only minimal coding effort, using simple npm commands, to create new applications with server and client components
  • Real-time collaboration: Facilitates real-time communication via WebSocket events, enabling sending, listening, and broadcasting messages to clients
  • Edge computing: Leverages edge computing networks to intelligently route and optimize connections from users across the globe, supporting scalability without developer intervention
  • Development language support: Supports modern JavaScript, TypeScript, npm modules, and WebAssembly modules
  • Developer tools: Includes local development tools, deploy previews, and secret management, enabling seamless integration with development workflows and stacks
  • Framework support: Supports popular collaboration frameworks like Y.js, automerge, and replicache, allowing quick addition of real-time collaboration features to existing apps

Building a multiplayer app with PartyKit

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.

Setting up 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 
Enter fullscreen mode Exit fullscreen mode

Creating the server

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;
Enter fullscreen mode Exit fullscreen mode

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 connect
  • onMessage(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

Deploying the application

Now, let’s deploy the application to the cloud using the following command:

npx partykit deploy server/index.ts --name vijit-ail-demo
Enter fullscreen mode Exit fullscreen mode

Deploying PartyKit Application 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.

Configuring the frontend

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>
Enter fullscreen mode Exit fullscreen mode
// 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 />);
Enter fullscreen mode Exit fullscreen mode

Run the following command to install the monaco-editor and the uuid package:

yarn add @monaco-editor/react monaco-editor uuid
Enter fullscreen mode Exit fullscreen mode

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.

Setting up the collaborative code editor

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}
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

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: Real-Time Code Editor Built PartyKit

PartyKit vs. Socket.IO

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:

  • Inbuilt collaboration framework support: One of the significant advantages of PartyKit is its inbuilt support for popular collaboration frameworks like Y.js, automerge, and replicache. This is particularly beneficial for developers working on applications that require real-time collaborative features, as it eliminates the need to manually integrate these frameworks, saving time and reducing complexity. Socket.IO does not offer this inbuilt support, requiring developers to manually incorporate any collaboration frameworks they wish to use
  • Edge computing: PartyKit leverages the power of edge computing to optimize connections and support a global user base without manual developer intervention. This is a significant advantage for applications with globally dispersed users, as it ensures low latency and a seamless user experience, regardless of user location. In contrast, Socket.IO does not have inbuilt support for edge computing, and optimizing connections for a global user base would require additional manual setup and infrastructure
  • Developer-friendly tools: PartyKit has a suite of developer-friendly tools, including local development tools, deploy previews, and secret management. These tools simplify the development process and integrate seamlessly with existing development workflows. Socket.IO, while offering a robust feature set, does not include these additional tools, which means developers may need to spend extra time setting up their development environment
  • Simplified development process: PartyKit is designed to streamline the development process as much as possible. Creating a new application with server and client components requires minimal coding effort and can be achieved with simple npm commands. Socket.IO, while manageable, often requires more setup and a deeper understanding of its API and features
  • Language support: PartyKit supports modern JavaScript, TypeScript, npm modules, and WebAssembly modules. This provides flexibility for developers and allows use of the tools and languages they are most comfortable with. Socket.IO primarily supports JavaScript, and while it can be used with TypeScript and other languages that compile to JavaScript, it does not have inbuilt support for WebAssembly modules

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.

Conclusion

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.


Get set up with LogRocket's modern React error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID.
  2. Install LogRocket via NPM or script tag. LogRocket.init() must be called client-side, not server-side.

NPM:

$ npm i --save logrocket 

// Code:

import LogRocket from 'logrocket'; 
LogRocket.init('app/id');
Enter fullscreen mode Exit fullscreen mode

Script Tag:

Add to your HTML:

<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>
Enter fullscreen mode Exit fullscreen mode

3.(Optional) Install plugins for deeper integrations with your stack:

  • Redux middleware
  • ngrx middleware
  • Vuex plugin

Get started now

Top comments (0)