Skip to content

Next.js Realtime prompt

Rules that make your coding agent build realtime features that survive production: Server-Sent Events done right, WebSockets where they can run, reconnection with backoff, presence and conflict handling.

What it makes your agent good at

Realtime code from a coding agent tends to work on localhost and fail in production: a socket server inside a Route Handler, an EventSource per component, no heartbeat, reconnect loops without jitter, and an in-memory event bus that serverless instances never share. This prompt gives the agent the transport that fits each feature, plus the parts tutorials skip: cleanup on disconnect, replay from Last-Event-ID, pub/sub fan-out, optimistic updates that reconcile, and cached server data that actually refreshes.

  1. The right transport per featureSSE from a Route Handler for server-to-client updates, a managed service or separate server for WebSockets, a refresh when seconds are fine.
  2. SSE that holds up in productionEvent ids, heartbeats, no-transform and X-Accel-Buffering headers, cleanup on abort and cancel, and replay from Last-Event-ID.
  3. One connection per tabA module-level store read with useSyncExternalStore, effects that clean up under Strict Mode, and useEffectEvent for fresh props.
  4. Reconnects that don't stampedeExponential backoff with full jitter, reconnects on online and visible, and a resync after every reconnect so no event is lost.
  5. Optimistic and consistentClient mutation ids with useOptimistic, server versions to order events, optimistic concurrency checks, and Yjs for shared text.
  6. Server data that refreshesrouter.refresh after an event, with updateTag or revalidateTag on the writer so cached reads actually change.

The Next.js Realtime rules

118 lines, current to Next.js 16.4 and React 19.3. Last reviewed October 23, 2026. Plain Markdown, no front matter: each agent's is below.

Download

nextjs-realtime.md

# Next.js Realtime

You are adding realtime features (live feeds, notifications, presence, chat, collaborative editing) to a Next.js 16 App Router project with React 19 and TypeScript. Write code for the versions in `package.json` and for the host the app deploys to: realtime code that works on localhost often breaks behind a proxy or on serverless functions.

## Before you write code

- Find where the app runs (a serverless host, a container, a long-running Node server) and what it already uses for realtime (a managed service's SDK, Redis, Supabase, a WebSocket server). Extend that instead of adding a second channel.
- Read `node_modules/next/dist/docs/01-app/01-getting-started/15-route-handlers.md` and `02-guides/streaming.md` (streaming Route Handlers, proxies, buffering).
- Check `next.config.ts` for `cacheComponents`: with it, a `GET` Route Handler that reads no request data is prerendered at build time.
- Pick the transport per feature. Server to client only (notifications, feeds, progress, dashboards): Server-Sent Events from a Route Handler, or `router.refresh()` on an interval when seconds of delay are fine. Both directions at low latency (typing, cursors, multiplayer, shared editing): WebSockets from a managed service or a separate server.

## Server-Sent Events from a Route Handler

```ts
// app/api/events/route.ts
export async function GET(request: Request) {
  const session = await getSession(); // reads cookies, so it runs per request
  if (!session) return new Response("Unauthorized", { status: 401 });

  const encoder = new TextEncoder();
  let stop = () => {};
  const stream = new ReadableStream<Uint8Array>({
    start(controller) {
      const send = (text: string) => {
        try {
          controller.enqueue(encoder.encode(text));
        } catch {
          stop();
        }
      };
      const unsubscribe = subscribe(session.user.id, (event) =>
        send(`id: ${event.id}\nevent: ${event.type}\ndata: ${JSON.stringify(event.data)}\n\n`),
      );
      const heartbeat = setInterval(() => send(": ping\n\n"), 15_000);
      stop = () => {
        clearInterval(heartbeat);
        unsubscribe();
      };
      send("retry: 3000\n\n");
      request.signal.addEventListener("abort", () => stop(), { once: true });
    },
    cancel() {
      stop();
    },
  });

  return new Response(stream, {
    headers: {
      "Content-Type": "text/event-stream; charset=utf-8",
      "Cache-Control": "no-cache, no-transform",
      "X-Accel-Buffering": "no",
    },
  });
}
```

`subscribe` is the project's pub/sub (see Scaling) and returns an unsubscribe function.

- An event is `event:`, `data:` and `id:` lines ending in a blank line; `JSON.stringify` keeps `data` on one line. Lines starting with `:` are comments: send one every 15 to 30 seconds so proxies and load balancers keep the connection open.
- Clean up on `request.signal` abort and in `cancel()`: clear timers, unsubscribe, release connections. Writing after a disconnect throws, so guard `enqueue`.
- On reconnect the browser sends the last `id:` as the `Last-Event-ID` header. Replay what came after it from a durable log, or send a `resync` event so the client refetches.
- `no-transform` and `X-Accel-Buffering: no` stop compression and nginx from buffering the stream. Keep the default Node.js runtime: no `runtime` export.
- With Cache Components, read cookies or headers (or `await connection()` from `next/server`) before streaming, or the handler is prerendered.
- `EventSource` sends cookies on same-origin requests but cannot set headers: authenticate with the session cookie, or use `fetch` with a stream reader when you need a token. Derive the channel from the session, never from a query parameter the client can change.
- On serverless hosts a connection lives at most `maxDuration`, and each open stream holds a function instance. Let `EventSource` reconnect when the host cuts it, and move to a managed service when idle connections dominate the bill.
- Over HTTP/1.1 a browser opens at most six connections per origin, shared by every tab: one `EventSource` per tab, and HTTP/2 in production.

## WebSockets

- Route Handlers cannot accept a WebSocket upgrade, and serverless functions end after the response, so never put a socket server inside a Next.js route or `proxy.ts`. Use a managed service (Pusher Channels, Ably, Liveblocks, PartyKit, Supabase Realtime) or a separate WebSocket server (`ws`, Socket.IO) on a long-running host.
- Authorize in a Route Handler: check the session, then sign the channel subscription or issue a short-lived token. The service's secret stays on the server; the browser gets the public key and the token. A channel name is not access control.
- Publish from the server after the write commits, in the Server Function or Route Handler that changed the data. Client-to-client events suit ephemeral state such as typing and cursors; validate them on receipt with Zod.

## Subscribing on the client

- Subscriptions live in Client Components: open them in an effect whose cleanup closes them, or keep them in a module-level store read with `useSyncExternalStore`. Strict Mode runs effects twice in development, so the cleanup must undo everything.
- One connection per tab: a module owns the `EventSource` or socket, counts subscribers per topic, and closes when the last one leaves. Components subscribe to topics; they never open connections.
- With `useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)`, `subscribe` returns its unsubscribe, `getSnapshot` returns the same reference until the data changes (a new array per call re-renders forever), and `getServerSnapshot` returns the server's initial value so hydration matches.
- Read the latest props inside event handlers with `useEffectEvent`, so changing a filter does not reopen the connection.
- Seed state with the data the Server Component rendered, then apply events. Show the connection state (live, reconnecting, offline) wherever stale data matters.

## Reconnection

- `EventSource` retries network drops itself, waiting the `retry:` delay, but closes for good when the server answers with an error status or another content type. In `onerror`, when `readyState === EventSource.CLOSED`, reconnect yourself with backoff, and stop once the user is signed out.
- Reconnect sockets, and a closed `EventSource`, with exponential backoff and full jitter: `Math.random() * Math.min(30_000, 1_000 * 2 ** attempt)`. Reset `attempt` once a connection has stayed up, cap the attempts, then show offline with a retry button.
- Reconnect at once on the `online` event and when the tab becomes visible again.
- After every reconnect, resync from the last event id or refetch the snapshot: missed events never arrive otherwise. Handle events idempotently: dedupe by id and drop events older than the version you hold.

## State, presence and conflicts

- Optimistic updates: give each mutation a client id (`crypto.randomUUID()`), apply it with `useOptimistic` in a transition that calls the Server Function, and include the id in the event the server publishes, so the confirmed item replaces the optimistic one instead of duplicating it. On failure the optimistic state reverts when the transition ends: show the error.
- The server orders events: each carries a server version or sequence per entity. Never order by client clocks.
- Last-write-wins by server version suits independent fields such as a status or a title. For records two people edit, use optimistic concurrency (`UPDATE … WHERE id = $1 AND version = $2`) and return a conflict when no row matches, so the client reloads and reapplies.
- For shared text and rich documents, use a CRDT: Yjs (`Y.Doc`, `Y.Text`, `Y.Map`) with a provider and the editor's binding. Persist encoded updates (`Y.encodeStateAsUpdate`), not plain text, and use awareness for cursors and selections.
- Presence is a heartbeat with a TTL (a Redis key that expires, or the service's presence API), never an `online` column in the main database or a single disconnect event. Throttle cursor and typing updates to one per animation frame or every 50 to 100 ms.

## Scaling and refreshing server data

- Serverless instances share no memory: an in-process `EventEmitter` only reaches clients on the same instance. Fan out through Redis pub/sub (a dedicated subscriber connection per instance), Postgres `LISTEN`/`NOTIFY` on a direct connection rather than a transaction pooler, or the managed service.
- Keep a short durable log for replay (Redis Streams, or an events table keyed by sequence); it is also your `Last-Event-ID` source.
- Send small events: ids, the changed fields and a version. Clients fetch large or private data through an authorized read.
- When server-rendered data changes, the receiving client calls `router.refresh()` from `useRouter`, debounced and inside `startTransition`. It does not clear the server cache: when the data is behind `use cache`, the writer invalidates first, with `updateTag(tag)` in a Server Function (or `refresh()` for uncached reads) and `revalidateTag(tag, "max")` or `revalidateTag(tag, { expire: 0 })` in a Route Handler or webhook.
- When the data lives in a client cache, write the event into it (TanStack Query's `setQueryData`, SWR's `mutate`) instead of refreshing the route.

## Do not

- Do not keep connected clients in a module-level array on a serverless host, or open a connection during render, in a Server Component or once per component.
- Do not poll in a tight `setInterval` loop, or reconnect on a fixed delay without jitter.
- Do not trust channel names, user ids or payloads from the client, broadcast whole records or secrets, or put private data on public channels.
- Do not call `revalidateTag` with one argument, or `updateTag` and `refresh()` from a Route Handler.

## Before you finish

- Run the type checker and the linter, and fix what they report.
- `curl -N` the SSE route: events arrive one by one, heartbeats keep coming, and it answers 401 without a session.
- With two browsers: a change in one appears in the other. Go offline in DevTools and back: it reconnects with backoff, without duplicate events, and catches up on missed ones.
- Close a tab and confirm the server stops its timers and subscriptions. In development, Strict Mode must still show one connection per tab.
- Unit-test the event reducer (order, duplicates, stale versions) and the backoff with fake timers.

10,434 characters, 1,566 words. Under Windsurf's 12,000-character limit for a rule, as every prompt here is.

Use it in your agent

The same rules, where your agent reads them. Each file has the front matter its agent needs, set to load when the task fits.

Claude Code

A rule it loads every session

  1. Save it as .claude/rules/nextjs-realtime.md in your project, front matter included, or run the install command.
  2. Claude Code loads every file in .claude/rules with your CLAUDE.md when a session starts.
  3. Prefer one file? Paste it into CLAUDE.md under its own heading instead.

.claude/rules/nextjs-realtime.md

# Next.js RealtimeYou are adding realtime features (live feeds, notifications, presence, chat, collaborative editing) to a Next.js 16 App Router project with React 19 and TypeScript. Write code for the versions in `package.json` and for the host the app deploys to: realtime code that works on localhost often breaks behind a proxy or on serverless functions.## Before you write code- Find where the app runs (a serverless host, a container, a long-running Node server) and what it already uses for realtime (a managed service's SDK, Redis, Supabase, a WebSocket server). Extend that instead of adding a second channel.- Read `node_modules/next/dist/docs/01-app/01-getting-started/15-route-handlers.md` and `02-guides/streaming.md` (streaming Route Handlers, proxies, buffering).- Check `next.config.ts` for `cacheComponents`: with it, a `GET` Route Handler that reads no request data is prerendered at build time.- Pick the transport per feature. Server to client only (notifications, feeds, progress, dashboards): Server-Sent Events from a Route Handler, or `router.refresh()` on an interval when seconds of delay are fine. Both directions at low latency (typing, cursors, multiplayer, shared editing): WebSockets from a managed service or a separate server.## Server-Sent Events from a Route Handler…

Or let the shadcn CLI write this file: sign in (free) for your command.

Cursor

A project rule with front matter

  1. Save it as .cursor/rules/nextjs-realtime.mdc, front matter included, or run the install command.
  2. Cursor attaches it when you work on matching files, or when the task fits its description.
  3. Check it under Cursor Settings, Rules, then commit the folder for your team.

.cursor/rules/nextjs-realtime.mdc

---description: "Adding realtime to Next.js: Server-Sent Events Route Handlers, WebSocket services, client subscriptions, reconnection, presence, optimistic updates and pub/sub."alwaysApply: false---# Next.js RealtimeYou are adding realtime features (live feeds, notifications, presence, chat, collaborative editing) to a Next.js 16 App Router project with React 19 and TypeScript. Write code for the versions in `package.json` and for the host the app deploys to: realtime code that works on localhost often breaks behind a proxy or on serverless functions.## Before you write code- Find where the app runs (a serverless host, a container, a long-running Node server) and what it already uses for realtime (a managed service's SDK, Redis, Supabase, a WebSocket server). Extend that instead of adding a second channel.- Read `node_modules/next/dist/docs/01-app/01-getting-started/15-route-handlers.md` and `02-guides/streaming.md` (streaming Route Handlers, proxies, buffering).- Check `next.config.ts` for `cacheComponents`: with it, a `GET` Route Handler that reads no request data is prerendered at build time.- Pick the transport per feature. Server to client only (notifications, feeds, progress, dashboards): Server-Sent Events from a Route Handler, or `router.refresh()` on an interval when seconds of delay are fine. Both directions at low latency (typing, cursors, multiplayer, shared editing): WebSockets from a managed service or a separate server.## Server-Sent Events from a Route Handler…

Or let the shadcn CLI write this file: sign in (free) for your command.

Windsurf

A workspace rule for Cascade

  1. Save it as .windsurf/rules/nextjs-realtime.md, or run the install command.
  2. trigger: model_decision lets Cascade pull it in when the task fits its description.
  3. Windsurf reads up to 12,000 characters per rule; this one is 10,434.

.windsurf/rules/nextjs-realtime.md

---trigger: model_decisiondescription: "Adding realtime to Next.js: Server-Sent Events Route Handlers, WebSocket services, client subscriptions, reconnection, presence, optimistic updates and pub/sub."---# Next.js RealtimeYou are adding realtime features (live feeds, notifications, presence, chat, collaborative editing) to a Next.js 16 App Router project with React 19 and TypeScript. Write code for the versions in `package.json` and for the host the app deploys to: realtime code that works on localhost often breaks behind a proxy or on serverless functions.## Before you write code- Find where the app runs (a serverless host, a container, a long-running Node server) and what it already uses for realtime (a managed service's SDK, Redis, Supabase, a WebSocket server). Extend that instead of adding a second channel.- Read `node_modules/next/dist/docs/01-app/01-getting-started/15-route-handlers.md` and `02-guides/streaming.md` (streaming Route Handlers, proxies, buffering).- Check `next.config.ts` for `cacheComponents`: with it, a `GET` Route Handler that reads no request data is prerendered at build time.- Pick the transport per feature. Server to client only (notifications, feeds, progress, dashboards): Server-Sent Events from a Route Handler, or `router.refresh()` on an interval when seconds of delay are fine. Both directions at low latency (typing, cursors, multiplayer, shared editing): WebSockets from a managed service or a separate server.## Server-Sent Events from a Route Handler…

Or let the shadcn CLI write this file: sign in (free) for your command.

GitHub Copilot

Instructions for the repository

  1. Save it as .github/instructions/nextjs-realtime.instructions.md, or run the install command.
  2. applyTo: "**" applies it to every request in this repository.
  3. Or paste it into .github/copilot-instructions.md, which Copilot reads for every request.

.github/instructions/nextjs-realtime.instructions.md

---description: "Adding realtime to Next.js: Server-Sent Events Route Handlers, WebSocket services, client subscriptions, reconnection, presence, optimistic updates and pub/sub."applyTo: "**"---# Next.js RealtimeYou are adding realtime features (live feeds, notifications, presence, chat, collaborative editing) to a Next.js 16 App Router project with React 19 and TypeScript. Write code for the versions in `package.json` and for the host the app deploys to: realtime code that works on localhost often breaks behind a proxy or on serverless functions.## Before you write code- Find where the app runs (a serverless host, a container, a long-running Node server) and what it already uses for realtime (a managed service's SDK, Redis, Supabase, a WebSocket server). Extend that instead of adding a second channel.- Read `node_modules/next/dist/docs/01-app/01-getting-started/15-route-handlers.md` and `02-guides/streaming.md` (streaming Route Handlers, proxies, buffering).- Check `next.config.ts` for `cacheComponents`: with it, a `GET` Route Handler that reads no request data is prerendered at build time.- Pick the transport per feature. Server to client only (notifications, feeds, progress, dashboards): Server-Sent Events from a Route Handler, or `router.refresh()` on an interval when seconds of delay are fine. Both directions at low latency (typing, cursors, multiplayer, shared editing): WebSockets from a managed service or a separate server.## Server-Sent Events from a Route Handler…

Or let the shadcn CLI write this file: sign in (free) for your command.

Codex & AGENTS.md

The file most agents read

  1. Paste it into AGENTS.md at your project's root, under its own heading.
  2. Codex reads AGENTS.md before it starts work, and so do Cursor, Zed, Jules, Amp and Copilot's coding agent.
  3. A folder can have its own AGENTS.md: the one closest to the file being edited wins.

AGENTS.md

## Next.js RealtimeYou are adding realtime features (live feeds, notifications, presence, chat, collaborative editing) to a Next.js 16 App Router project with React 19 and TypeScript. Write code for the versions in `package.json` and for the host the app deploys to: realtime code that works on localhost often breaks behind a proxy or on serverless functions.### Before you write code- Find where the app runs (a serverless host, a container, a long-running Node server) and what it already uses for realtime (a managed service's SDK, Redis, Supabase, a WebSocket server). Extend that instead of adding a second channel.- Read `node_modules/next/dist/docs/01-app/01-getting-started/15-route-handlers.md` and `02-guides/streaming.md` (streaming Route Handlers, proxies, buffering).- Check `next.config.ts` for `cacheComponents`: with it, a `GET` Route Handler that reads no request data is prerendered at build time.- Pick the transport per feature. Server to client only (notifications, feeds, progress, dashboards): Server-Sent Events from a Route Handler, or `router.refresh()` on an interval when seconds of delay are fine. Both directions at low latency (typing, cursors, multiplayer, shared editing): WebSockets from a managed service or a separate server.### Server-Sent Events from a Route Handler…

AGENTS.md is shared with your own instructions, so paste the rules in: the copy sets their headings a level down.

Rules teach it. The MCP equips it. With Pro, your agent installs every component, block and icon it needs, from a prompt.

Get Pro

Pricing

Give your agent the parts, with Pro

The prompts are free. Every Pro plan adds the MCP server, so your agent installs the components, blocks and icons it writes code around.

4.9/5 out of 5 · 150+ reviewsfrom 150+ founders and developers.

Billed once a year in USD. Cancel anytime.

Individual

For personal projects & open source

$32instead of $49/mo

$389 billed yearly

  • How many people can use the license.Seats: 1 seat
  • Client work, SaaS products and anything you charge for.Commercial use: Personal & open source only
  • Page sections in 56 categories, from heroes and pricing tables to dashboards and settings, on every framework and UI library, in your theme.Blocks: All 6,181 blocks
  • Complete Next.js sites for startups, SaaS, agencies and portfolios, every page designed, ready to ship.Templates: All 14 templates
  • Your AI agent searches, previews and installs any component, block, icon or DESIGN.md from a prompt: 22 tools in Claude Code, Cursor, VS Code and 35 more clients.MCP server: 38 AI clients
  • Buttons with motion and states of their own, from an arrow that slides in to a burst of confetti.Animated buttons: All 35 buttons
  • Typewriters, scrambles, counters and more, for headlines, labels and numbers.Text effects: All 97 effects
  • Animated backgrounds for heroes, sections and cards, from a soft aurora to a flock that flees the cursor. Most are pure CSS.Backgrounds: All 100 backgrounds
  • WebGPU shaders, from a slow aurora to a black hole, each drawing only while it's on screen.Shaders: All 100 shaders
  • One design and one theme on all four: the components, blocks and effects are built for each framework.Frameworks: React, Vue, Svelte, Solid
  • React on Base UI, Radix UI, React Aria or Ark UI: install the version for the library your project already uses. Vue, Svelte and Solid run on Ark UI.UI libraries: Base UI, Radix, Aria, Ark
  • From a button to a date picker, a color picker or a guided tour: accessible, typed in TypeScript, each with live demos.Components: All 174 components
  • Real-world patterns built from the components, in 53 galleries: forms, tables, menus, dialogs and more.Examples: All 1,103 examples
  • Area, bar, line, pie, radar and radial charts on TanStack Charts, on every framework. React also gets the Recharts originals.Charts: All 70 charts
  • Debounce, local storage, media queries and more: each hook is one typed file you own.React hooks: All 35 hooks
  • From 222 libraries, Lucide, Heroicons, Tabler and Material Symbols among them, as SVG or a React component in one command.Icons: 294k+ icons
  • Stripe, Linear, Apple and 497 more: a brand's colors, typography and components in one file your AI agent builds from.DESIGN.md files: All 500 brands
  • Restyle every component and block at once: 8 styles, 7 grays, 18 colors and 5 radii, shared as a shadcn preset code.Theme builder: 8 styles × 18 colors
  • Priority requests are answered first.Support: Standard

Team

Most popular

For freelancers, agencies & startups

$66instead of $99/mo

$789 billed yearly · ≈ $13/dev/mo

Everything in Individual, plus

  • How many people can use the license.Seats: Up to 5 seats
  • Client work, SaaS products and anything you charge for.Commercial use: Included
  • Priority requests are answered first.Support: Priority

Organization

For companies with larger teams

$132instead of $199/mo

$1,589 billed yearly

Everything in Team, plus

  • How many people can use the license.Seats: Unlimited seats
  • Secure checkout via Stripe
  • Code lives in your repo
  • Keep what you've shipped if you cancel
  • License terms

Next.js Realtime prompt FAQ

What the Next.js Realtime prompt covers and how to use it in your agent.

How does the Next.js Realtime prompt reconcile optimistic updates?

Each mutation gets a client id and is applied with useOptimistic in a transition that calls the Server Function. The server publishes the event with the same id and a version, so the confirmed item replaces the optimistic one, and a failed write reverts when the transition ends.

How does the Next.js Realtime prompt resolve conflicting edits?

Last-write-wins by server version for independent fields, optimistic concurrency with a version check for records two people edit, and a CRDT with Yjs for shared text and rich documents. Events are always ordered by the server, never by client clocks.

How does the Next.js Realtime prompt refresh server-rendered data after an event?

The client that receives the event calls router.refresh, debounced and inside a transition. Because that does not clear the server cache, the code that wrote the data first calls updateTag in a Server Function, or revalidateTag with a profile in a Route Handler or webhook.

4.9 out of 5 from 150+ developers

Rules teach it. The MCP equips it.

Every component, block and icon, installed by your AI agent on your stack. The prompts stay free.

Get Pro
  • Cancel anytime
  • Keep what you've shipped