WebSockets in React Server Components: Client Islands
Why you shouldn’t copy server state into a global store for realtime Hot take: when React Server Components (RSC) are your default, the worst thing you can do for realtime features is turn an entire page into a client bundle. Copying server state into a global client store via a page-level "use client" wrapper hands away server-rendered HTML, streaming, and tiny JS budgets — and raises…
When using React Server Components (RSC), a common mistake is to copy server state into a global client store. This approach dispels the benefits of server-rendered HTML, streaming, and tiny JS budgets, leading to larger client bundles, broader re-renders, and poorer Interaction to Next Paint (INP). Instead, ship pages as Server Components and isolate WebSockets in minimal client islands that push updates into a cache.
To begin, change the page to a Server Component that fetches and renders HTML on the server. When implementing a WebSocket subscription, add a small client island (5-20 lines) that writes into your client cache using TanStack Query, SWR, or a custom store. This local component rehydrates only what has changed, limiting unnecessary re-renders and preserving the streaming benefits of RSC.
Key advantages include smaller shipped JS, fewer unnecessary re-renders, and faster first interactions. However, drawbacks involve needing a robust cache strategy (TanStack Query or SWR) to handle stale time, invalidation, and optimistic updates. Debugging becomes more complex since state is distributed between server renders and client caches.
Best practices involve prefetching initial data on the server, hydrating the client cache via TanStack Query’s dehydrate and HydrationBoundary, and using setQueryData for surgical updates when dealing with small deltas. When implementing, convert large UI components to Server Components and add a 5-line RealtimeSubscriber.client.tsx island for the WebSocket subscription.
To isolate realtime features effectively, prioritize notifications, presence indicators, small activity feeds, or live ticks that send small deltas. Avoid sending entire datasets through the socket, as this can lead to significant JS bundle sizes and slower performance. Begin by isolating one realtime feature into a 5-line client island and measure the impact on bundle size, hydration time, and INP.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.