Recipes
Two kinds of page live here. The real-world guides build a whole app, client and server, and show what happens when the network misbehaves. The patterns below them are short and self-contained: copy what you need.
Real-world guides
Each guide's code is complete and type-checked against the library, and was run in Chrome against its server, with stand-ins for third-party APIs where a guide uses one.
AI apps
AI chatbot
Streamed answers that survive reconnects and reloads, a Stop button that stops the model, and tools the user approves. The server calls Claude.
createWebSocketRuns · useAgentRun · createWebSocketResponder · useAgentApproval
AI image and video generator
A queue with live positions, progress and previews, jobs that carry on through reloads, and one gallery on every device.
createWebSocketRuns · useAgentRun · createWebSocketState · heartbeat
Voice assistant and text to speech
Microphone audio up, the answer down, interruptions cut back to what was heard, and narration that streams and downloads.
volatile · maxBuffered · priority · createWebSocketRequests
Multiplayer games
Multiplayer party game
A trivia host screen and phones: rooms with a join code, a countdown every screen agrees on, answers counted once, and phones that rejoin after they sleep.
createWebSocketRequests · createWebSocketPresence · createWebSocketLatency · createWebSocketState
Real-time action game
The game example, file by file: a fixed-tick server, prediction and interpolation, an adaptive send rate and a 60 fps canvas.
react-websocket/timeline · createWebSocketSendRate · createWebSocketLatency · react-websocket/chaos
Collaboration
Team chat
Channels where every message arrives once and in order, an outbox that survives reloads, unread counts on every device, and one connection for all tabs.
createWebSocketFeeds · react-websocket/outbox · createWebSocketPresence · react-websocket/tabs
Collaborative whiteboard
Live cursors, strokes others watch as you draw, a snapshot for late joiners, drawing offline, and notes merged by Yjs.
createWebSocketPresence · createWebSocketSendRate · createWebSocketFeeds · react-websocket/outbox
Live data
Trading dashboard
A 500-symbol watchlist that streams only the rows on screen and renders once per frame, an order book that stays exact through lost messages, and orders placed exactly once.
createWebSocketTopics · createWebSocketFeeds · useInView · batch: 'frame'
Live notifications
One connection for every tab, an unread badge that resumes from the last notice after a reload, reads that survive going offline, and sign-out everywhere.
react-websocket/tabs · createWebSocketFeeds · react-websocket/outbox · useWebSocketOutbox
Delivery tracking
A courier marker that glides between GPS fixes without React renders, an ETA on the server's clock, and proof of delivery that gets through a dead zone.
react-websocket/timeline · createWebSocketLatency · createWebSocketSendRate · react-websocket/outbox
Many message types on one socket
Pub/sub topics, Django Channels groups and similar features all deliver different kinds of message over the same socket. Tag each message on the server with a discriminant field, type it as a union, and route on the client:
Each component listens for what it cares about, and they all share one connection. To validate the union at runtime, pass a schema.
Request/response
WebSockets have no built-in replies. A common convention is to send an id with each request and have the server echo it in the reply. This helper wraps that in a promise with a timeout:
If the socket isn't open, the request is queued and sent on connect. The timer starts immediately, so pick a timeout that covers a reconnect. If the connection drops after the request was sent, the server may never reply; the timeout turns that into a rejection.
Following a changing URL
With useWebSocket(url), a new URL closes the old connection and opens the new one. Build the URL from state:
Each distinct URL is its own shared connection. If the URL comes from a text field, debounce it so you don't connect on every keystroke.
To switch rooms on one connection instead, keep a single client and let topic subscriptions send the join/leave messages:
Calling send from an effect yourself gets two things wrong. The server forgets rooms when the connection drops, so nothing joins them again after a reconnect. And send queues while disconnected, so a leave for a room you switched away from while offline goes out after the reconnect, for a room the new connection never joined. Topics join every held room again each time the socket opens, and never queue join or leave.
Presence and typing indicators
Send typing events at most every couple of seconds, and keep the list of who's typing in state:
Polling over a socket
The server decides when to push. If you need to ask on an interval, call send from your own timer:
Optimistic sends
send() never throws for a disconnected socket: it queues the message and returns false. Show the message immediately, mark whether it actually went out, and confirm it when the server echoes it back:
Queued messages go out in order when the connection opens. To bound memory during a long outage, set queue: { limit }. close() and giving up both clear the queue, so messages still marked queued at that point were never sent.
Pausing a connection
enabled: false releases a component's hold on the connection. When no other component holds it, it closes; enabled: true connects again:
A null URL works the same way: useWebSocket(isLoggedIn ? url : null).
Content hidden by React's <Activity> releases its connections automatically.
Logging and telemetry
The logger option receives the client's debug messages: connection attempts, retry delays, heartbeat timeouts and dropped messages. Route it to your telemetry, and listen to events for structured data:
In development, logger: console.debug is often all you need.
Hook URL or shared client?
Start with useWebSocket(url) for a socket used by one component. Move to a shared client when several components, non-React code, or a URL function are involved.