The RSC wire format, React Flight serialisation, how the client reconciler processes RSC payloads, React 19 async params/searchParams as Promises, and the React Compiler's automatic memoisation model.
A-1 — RSC Internals: The React Flight Protocol and React 19
Who this is for: Architects who want to understand what's actually happening when a Next.js App Router page renders. This module goes below the abstraction — into the React Flight wire format, how the client reconciler processes RSC payloads, why client-side navigation in the App Router preserves Client Component state, and what React 19 changed. This is the layer that makes everything else in the Architect phase make sense.
What React Flight Actually Is
When engineers say "React Server Components," they're describing a rendering model. When they ask what's transmitted over the wire, the answer is React Flight — a serialisation protocol designed specifically for streaming React component trees from server to client.
Flight is not JSON. It's not HTML. It's a line-delimited text format where each line represents one unit of work in the React tree. Understanding its structure is how you understand why RSC works the way it does and why it has the constraints it has.
A minimal Flight payload looks like this:
Line by line:
1:I[...]— Module reference.Imeans import. This declares that a Client Component exists at the given file path and chunk. It never contains the component's source code — only a reference.0:[...]— React element tree. The server's rendered output, as a JSON-like structure."$1"references the module declared on line 1. The server rendered everything it could; where a Client Component appears, it left a placeholder that points to the client bundle.
This is the key insight: the server renders Server Components to their final output. For Client Components, it emits a module reference ($1, $2, etc.) and stops. The client downloads those module references, renders the Client Components in the browser, and stitches the result into the tree React received from the server.
The Payload Type System
React Flight uses single-letter prefixes to identify what each line contains:
| Prefix | Meaning |
|---|---|
I | Import — a module reference to a Client Component chunk |
M | Module — similar to I, alternative format |
T | Text — a large string value, usually inline HTML |
S | Symbol — a React special value (Suspense, Fragment, etc.) |
E | Error — a thrown error (for error boundary activation) |
H | Hint — a preload/prefetch directive |
Suspense boundaries in the payload cause the server to flush a partial payload immediately, with a placeholder for the pending content. When the async data resolves, the server writes another chunk with the filled-in content. The client reconciler knows how to patch the placeholder without re-rendering the static parts of the tree.
How the Client Reconciler Processes a Flight Payload
When you navigate in the App Router, the browser doesn't receive HTML — it receives a Flight payload for the new page's Server Components. React processes it through createFromFetch() (or createFromReadableStream() for streaming responses):
Step 6 is what makes App Router navigation feel different from a page reload. When you navigate from /dashboard to /dashboard/settings, React doesn't unmount and remount the dashboard layout Client Components. It reconciles the server-rendered output of the new page against the existing tree, leaving any Client Component state intact.
This is why a Client Component inside a persistent layout doesn't lose its state during navigation — the reconciler sees it's the same component at the same position in the tree and preserves it.
Sign in to keep reading
The rest of this module is free — sign in with Google to unlock it and track your progress.
Sign in & RegisterDiscussion
0Join the discussion