What Actually Crosses the React Server Component Boundary
A deep dive into which prop types survive React 19's Flight serializer across the client boundary, and two subtle traps that break builds despite passing review.
This piece reveals that the "use client" boundary isn't really a type system but a set of runtime rules enforced by React 19's Flight serializer. Values like Date, Set, Map, Promise, typed arrays, Blob/File, FormData, ReadableStream, global symbols, and client/server references all cross safely — even Error objects cross, though they arrive empty in production. Meanwhile RegExp, URL, WeakMap, class instances, and null-prototype objects get rejected, since the check is prototype-based: even a plain data-only class instance throws with "Classes or null prototypes are not supported."
The article walks through two common traps. First, an innocent-looking config object like options={{ onSelect: fn }} actually smuggles a function past review — the serializer descends into the object, hits the arrow function, and fails at prerender, with an error message that references the object key rather than the JSX prop name. Second, the static-analysis tool rsc-gate had two false positives in its own history: flagging aliased imports as "server-only" leaks without resolving them, and wrongly rejecting global symbols (Symbol.for) that React actually allows. Both cases underline that breaking healthy code with a false-positive CI failure is more dangerous than missing a real leak.
For engineers, the takeaway is that this boundary's behavior is invisible in code review and only surfaces at the serializer's runtime logic — which is why static tools like rsc-gate, built on the TypeScript compiler API without needing a full build or .next/ output, matter for catching these issues early.