Claude Code for PartyKit — Workflow Guide
The Setup
You are building real-time multiplayer features using PartyKit, a platform for deploying collaborative servers that manage WebSocket connections, state, and room-based coordination. Claude Code can generate PartyKit servers and client integrations, but it defaults to raw WebSocket code or Socket.io patterns instead of PartyKit’s server-per-room model.
What Claude Code Gets Wrong By Default
-
Writes Socket.io server code. Claude generates
io.on('connection', socket => { })with Socket.io. PartyKit exports aServerclass withonConnect,onMessage, andonClosemethods — a fundamentally different API. -
Creates a single global WebSocket server. Claude builds one server handling all connections. PartyKit creates isolated server instances per room — each room has its own state, its own connections, and its own lifecycle.
-
Manages WebSocket connections manually. Claude tracks connections in a
Set, handles ping/pong, and writes reconnection logic. PartyKit manages connection lifecycle automatically —this.room.getConnections()returns all active connections for a room. -
Deploys on a traditional Node.js host. Claude writes Dockerfile and deployment configs for a WebSocket server. PartyKit deploys with
npx partykit deployto its edge infrastructure — no Docker or server management needed.
The CLAUDE.md Configuration
# PartyKit Real-Time Project
## Architecture
- Real-time: PartyKit (room-based multiplayer servers)
- Server: party/server.ts exports PartyKitServer class
- Client: PartySocket from partysocket package
- Deploy: npx partykit deploy
## PartyKit Rules
- Server exports: onConnect, onMessage, onClose, onRequest methods
- Each room is an isolated server instance with own state
- Broadcast: this.room.broadcast(message, [excludeIds])
- Get connections: this.room.getConnections()
- Persistent storage: this.room.storage.get/put (Durable Object-backed)
- Room ID from URL: partykit.io/party/[room-id]
- Client: new PartySocket({ host, room: "room-id" })
## Conventions
- Server in party/ directory (party/server.ts)
- Multiple party servers: party/chat.ts, party/cursor.ts
- Client connection in hooks: usePartySocket from partysocket/react
- Room IDs follow pattern: "doc:{docId}", "room:{roomId}"
- Messages are JSON strings: JSON.stringify/parse
- Static assets served from public/ directory
- Config in partykit.json at project root
Workflow Example
You want to build a collaborative cursor tracking feature. Prompt Claude Code:
“Create a PartyKit server that tracks cursor positions for all users in a room. When a user moves their cursor, broadcast the position to all other users. Include the React hook for connecting and sending cursor updates.”
Claude Code should create a party/cursor.ts server with onMessage that parses cursor positions and broadcasts to other connections using this.room.broadcast(message, [sender.id]), and a React hook using usePartySocket that sends cursor position on mouse move and maintains a map of other users’ cursor positions from incoming messages.
Common Pitfalls
-
Stateless server assumptions. Claude stores room state in module-level variables. PartyKit server instances can be evicted and recreated. Use
this.room.storage.put()for state that must survive server restarts, not in-memory variables. -
Room naming collisions. Claude uses simple room names like “main” or “default”. In production, room IDs should include context (user ID, document ID) to prevent unrelated users from sharing state:
doc:${docId}instead ofdoc. -
Message serialization issues. Claude sends JavaScript objects directly through WebSocket. PartyKit requires string messages — always
JSON.stringify()on send andJSON.parse()on receive. Sending objects silently converts them to[object Object].