Outcomes
- One React render per animation frame, even when the WebSocket bursts hundreds of messages per second
- One shared WebSocket per session, regardless of how many components subscribe to the live feed
- Zero perceptible jank through reconnects, sequence gaps, and snapshot recovery
- No layout shift between loading and live states — the UI is visually stable from first paint
- Server-rendered shell keeps the initial bundle small; hydration is scoped to the live islands only
What we shipped
A live SOL-PERP order book and trading surface engineered for the part of the day when markets matter most: high message rates, partial network failures, and brief upstream stalls. The market-data path stays consistent under load, the React tree never re-renders more than once per frame, and degraded states are visible to the user instead of silent.
How it works
Hot path outside React. The canonical book lives in mutable Map<priceKey, Level> structures inside a controller — not in useState. Raw WebSocket frames mutate the state directly, with no React allocations on the hot path. React only ever sees a derived snapshot.
One render per animation frame. Every message that arrives within a frame is coalesced into a single requestAnimationFrame publish. The selector sorts, slices, and pre-formats the visible depth once per frame; React reads that snapshot via useSyncExternalStore. No matter how bursty the feed, the UI renders at most once per paint.
useSyncExternalStore, not Context. Context broadcasts every value change to every subscriber — a poor fit for a hot live feed. useSyncExternalStore lets us subscribe explicitly with reference-equal snapshots, so unrelated re-renders don't propagate when the snapshot identity is preserved.
Singleton controller with refcount. A module-scoped controller is shared across the whole app. The first subscriber opens the WebSocket; later subscribers reuse it; the last unmount closes it. Two components watching the live book never produce two sockets.
Resilient transport. Exponential backoff with jitter, browser online/offline awareness, stale-feed monitoring, and non-retryable close-code handling. Sequence gaps trigger a forced reconnect and snapshot resync. A snapshot recovery watchdog auto-restarts the socket and only escalates to a page reload if recovery repeatedly fails.
Server shell, client islands. Layout, panel chrome, and static typography render on the server with no JavaScript shipped. Only the live badge, metrics grid, and order book tables are client components. The connection badge sits inside the server-rendered header without dragging the whole header into hydration.
Domain-isolated logic, fully tested. Parser, reducer, and socket lifecycle are pure modules with their own unit tests — covering snapshot/delta flow, sequence-gap handling, dropped-stale policy, reconnect behavior, and offline transitions. The controller orchestrates them; protocol logic never leaks into UI.
Loading without layout shift. Side tables render exactly the visible-depth count of skeleton rows during load, with the same row height as live rows. The error slot has reserved vertical space. The shell has a minimum height. The transition from loading to live is invisible.
Stack
Next.js 16 (App Router · React Compiler · Turbopack) · React 19 with useSyncExternalStore · TypeScript · native WebSocket · zod for runtime payload validation · CVA + Tailwind · Vitest