
Template details
Nitro + WebSockets Starter
Realtime live cursors, presence, and emoji reactions over a WebSocket, built with Nitro v3, React, and shadcn/ui.
About this template
The Nitro + WebSockets Starter is a realtime boilerplate from the Vercel examples repository that demonstrates live presence, shared cursors, and emoji reactions over a single WebSocket connection, built with Nitro v3, React, and shadcn/ui.
What is the Nitro + WebSockets Starter?
This starter ships a minimal realtime app: a React single-page application (Vite) on the frontend and a Nitro v3 server with native WebSocket support, powered by crossws, on the backend. It takes a WebSocket connection at /api/ws and produces a live collaborative room where every client sees cursor movements, join/leave events, and emoji reactions. The realtime layer runs entirely on Vercel Functions using the WebSockets beta, with no environment variables and no external services. The project is published under the MIT license as part of the official Vercel examples repository.
Key Features
- Native WebSocket handling — A single
defineWebSocketHandlerinserver/api/ws.tsworks in local dev and production; on Vercel the preset'scrossws/adapters/vercelbridge handles the upgrade, so there is no Vercel-specific code path. - Room-based pub/sub — Every connection subscribes to one
roomtopic, and events like cursor moves and reactions are broadcast with crossws'speer.publish— no external store and no client SDK. - Shared TypeScript wire protocol —
shared/types/realtime.tsdefinesClientMessage,ServerMessage, andPeertypes used by both client and server, keeping message handling in sync. - Reconnect with exponential backoff — On disconnect, the client reconnects automatically and reloads the roster from the
welcomeframe; a heartbeat (ping/pong) detects half-open connections and forces a reconnect. - Anonymous per-connection identity —
server/utils/identity.tsgenerates a name and color for each client, replaceable with an authenticated user for production. - Room isolation from the path — Nitro derives a pub/sub namespace from the connection path, so per-room routes work out of the box.
- shadcn/ui on Base UI primitives — UI components are shadcn/ui components built on Base UI, and a fresh project can be scaffolded with
pnpm dlx shadcn@latest init -b base. - One-command scaffolding — Clone the starter with
npx giget@latest gh:vercel/examples/websockets/nitro my-realtime-app, then runpnpm install && pnpm dev.
Who is it for?
- Developers new to WebSockets on Vercel — Use this starter to see a working WebSocket upgrade and realtime pub/sub pattern without managing separate infrastructure.
- Teams adding collaborative features to React apps — Extend the existing
ClientMessageandServerMessagevariants to add custom realtime interactions such as chat or drawing. - Nitro users exploring the Vite plugin — See how the
nitro/viteplugin serves the React SPA for unmatched routes while matching server routes like/api/wsfirst.
What can you do with it?
- Create a realtime demo — Launch the app in two browser tabs and watch live cursors, presence, and emoji reactions over a single WebSocket.
- Prototype collaborative tools — Add new message types to the shared wire protocol and handle them in
server/api/ws.tsto build features like shared whiteboards or multi-user forms. - Build multi-room applications — Key the pub/sub topic by a room id or rely on the path-derived namespace to isolate rooms, enabling per-URL chat or telepresence.
How does it work?
A Vercel Function accepts a WebSocket upgrade and keeps the connection open. Nitro v3 enables the feature with one flag in vite.config.ts: nitro({ features: { websocket: true } }). The handler at server/api/ws.ts manages subscriptions, broadcasts with peer.publish, and sends a welcome frame containing the current roster when a client connects.
FAQ
Is the Nitro + WebSockets Starter free?
Yes. The source is part of the vercel/examples repository and is released under the MIT license. Deploying to Vercel uses the Vercel Functions WebSockets beta; there are no paid add-ons or API keys required for the realtime layer.
Does it require a realtime client SDK?
No. The frontend's use-realtime.ts hook opens a WebSocket directly to /api/ws and parses the custom message types. The only dependency for realtime is the browser's native WebSocket API.
What happens when a Vercel Function reaches its maximum duration?
The WebSocket connection closes. The client automatically reconnects with exponential backoff and rebuilds the roster from the welcome frame on each new connection, so the app recovers without manual refresh.
Can I add authentication?
Yes. Replace the anonymous identity logic in server/utils/identity.ts with your authenticated user, and add any token checks you need in the WebSocket handler or upgrade step.
















