Claude Code for Liveblocks — Workflow Guide
The Setup
You are adding real-time collaborative features to a React application using Liveblocks. This means presence indicators, live cursors, and shared document state that syncs between users without manual WebSocket management. Claude Code can generate the room setup, presence hooks, and storage mutations, but it tends to reinvent WebSocket plumbing instead of using the Liveblocks abstractions.
What Claude Code Gets Wrong By Default
-
Builds custom WebSocket connections. When asked for real-time features, Claude writes raw
ws://connection code. Liveblocks handles all transport internally through itsRoomProviderand hooks. -
Uses wrong hook names. Claude generates
useLiveblocks()oruseRoom()calls that do not exist. The correct hooks areuseOthers(),useSelf(),useStorage(), anduseMutation()from@liveblocks/react. -
Ignores the liveblocks.config.ts file. Liveblocks requires a typed configuration file that defines
Presence,Storage, andUserMetatypes. Claude skips this and loses all type safety. -
Stores collaborative state in React state. Claude puts shared data in
useStateand tries to broadcast changes. Liveblocks uses its own CRDT-based storage accessed throughuseStorage()— React state is only for local UI.
The CLAUDE.md Configuration
# Liveblocks Collaborative App
## Architecture
- Framework: Next.js 14 with App Router
- Real-time: Liveblocks (@liveblocks/react, @liveblocks/node)
- Config: liveblocks.config.ts at project root defines all types
- Auth: /api/liveblocks-auth endpoint for token generation
## Liveblocks Rules
- All collaborative state goes through useStorage(), never useState
- Presence data uses useSelf() and useOthers() hooks
- Mutations use useMutation() hook with (root, args) => signature
- RoomProvider wraps collaborative pages, not the entire app
- Storage types must be defined in liveblocks.config.ts
- Use LiveList, LiveMap, LiveObject for nested collaborative data
- Never directly modify storage objects — use .set(), .push(), .delete()
## Conventions
- Room IDs follow pattern: "project:{projectId}"
- Presence includes cursor position and user color
- Storage mutations are colocated with the component that uses them
- Never import from @liveblocks/client directly in components
Workflow Example
You want to add live cursor tracking to a collaborative canvas. Prompt Claude Code:
“Add live cursor presence to the Canvas component. Show colored cursors for all connected users with their names. Update cursor position on mouse move.”
Claude Code should update the Presence type in liveblocks.config.ts to include cursor: { x: number, y: number } | null, use useUpdateMyPresence() in the mouse move handler, and render other cursors using useOthers() with a cursor SVG component.
Common Pitfalls
-
Forgetting to set cursor to null on mouse leave. Claude tracks mouse position but never clears it when the cursor leaves the canvas, causing ghost cursors that stick in the last position for other users.
-
Throttling presence updates wrong. Claude adds
debounceto cursor updates, which makes cursors jump. Liveblocks already throttles internally — adding your own debounce on top creates a laggy experience. -
Nesting LiveObjects incorrectly. Claude creates plain JavaScript objects inside storage mutations. Nested collaborative objects must be wrapped in
new LiveObject({}),new LiveList([]), ornew LiveMap()to be reactive.