Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/cloudflare/partykit/llms.txt

Use this file to discover all available pages before exploring further.

This guide walks you through creating a minimal real-time WebSocket server that broadcasts every incoming message to all connected clients. By the end you will have a running Worker on Cloudflare with a browser client that connects to it via PartySocket.
1

Install the packages

Create a new Workers project and install partyserver for the server side and partysocket for the browser client.
npm create cloudflare@latest my-party-app -- --type worker
cd my-party-app
npm install partyserver partysocket
partyserver is a zero-dependency library. partysocket is equally lean and works in browsers, Node.js, React Native, Deno, and Bun.
2

Write the Server class

Replace the contents of src/index.ts with the following. The Server class you export is a Durable Object subclass — PartyKit wires up the WebSocket plumbing so you only implement the hooks you care about.
// src/index.ts

import { routePartykitRequest, Server } from "partyserver";

// Define your Server
export class MyServer extends Server {
  onConnect(connection) {
    console.log("Connected", connection.id, "to server", this.name);
  }

  onMessage(connection, message) {
    console.log("Message from", connection.id, ":", message);
    // Send the message to every other connection
    this.broadcast(message, [connection.id]);
  }
}

export default {
  // Set up your fetch handler to use configured Servers
  async fetch(request: Request, env: Env): Promise<Response> {
    return (
      (await routePartykitRequest(request, env)) ||
      new Response("Not Found", { status: 404 })
    );
  },
} satisfies ExportedHandler<Env>;
Key points:
  • this.name — the room name resolved from the URL, available in every hook.
  • this.broadcast(message, [connection.id]) — sends message to all clients, excluding the sender.
  • routePartykitRequest — matches incoming requests to your Durable Object bindings automatically; returns null for non-matching paths so your fallback Response handles them.
3

Configure wrangler.jsonc

Open wrangler.jsonc and add durable_objects bindings and a migration entry. The binding name must match the exported class name exactly (case-insensitive match is done by routePartykitRequest after kebab-casing).
{
  "name": "my-party-app",
  "main": "src/index.ts",
  "compatibility_date": "2025-01-01",
  "durable_objects": {
    "bindings": [
      {
        "name": "MyServer",
        "class_name": "MyServer"
      }
    ]
  },
  "migrations": [
    {
      "tag": "v1",
      // new_sqlite_classes gives each Durable Object instance its own SQLite database
      "new_sqlite_classes": ["MyServer"]
    }
  ]
}
The "tag" value must be unique for each migration entry. If you add more Durable Object classes later, bump the tag (e.g. "v2") and add a new migration object — do not reuse existing tags.
4

Connect from the browser with PartySocket

Create a client-side file (or add to an existing frontend build) that uses PartySocket to connect. PartySocket automatically constructs the /parties/:party/:room URL that routePartykitRequest expects, and handles reconnections transparently.
// client.ts (runs in the browser)

import { PartySocket } from "partysocket";

const socket = new PartySocket({
  // Your deployed Worker URL; defaults to window.location.host in production
  host: "https://my-party-app.<your-subdomain>.workers.dev",
  // Kebab-cased version of your binding name (MyServer → my-server)
  party: "my-server",
  // The room name — one Durable Object instance per unique name
  room: "my-room",
});

socket.addEventListener("open", () => {
  console.log("Connected to room:", socket.room);
  socket.send("Hello, room!");
});

socket.addEventListener("message", (event) => {
  console.log("Received:", event.data);
});

socket.addEventListener("close", () => {
  console.log("Disconnected");
});
party is the kebab-cased version of your Durable Object binding name. MyServer becomes my-server, ChatRoom becomes chat-room, and so on. routePartykitRequest performs this conversion automatically on the server side.
5

Deploy with Wrangler

Run the Wrangler deploy command from your project root. Wrangler bundles your Worker, applies the Durable Object migration, and publishes everything to Cloudflare’s edge.
npx wrangler deploy
You should see output similar to:
✅ Successfully published your script
   https://my-party-app.<your-subdomain>.workers.dev
To run locally during development, use:
npx wrangler dev
Wrangler’s local dev environment simulates Durable Objects in-process, so you can iterate quickly before deploying. Your PartySocket client can point to localhost:8787 during development.

What’s next?

  • Enable hibernation to reduce costs when rooms are idle.
  • Use getConnectionTags and getConnections(tag) to filter broadcasts to subsets of clients.
  • Add onRequest to handle HTTP requests inside the same Durable Object.
  • Explore y-partyserver for real-time collaborative text editing with Yjs.

Build docs developers (and LLMs) love