React Flight Protocol

The introduction of React Server Components (RSC) marked a paradigm shift in how we build React applications, allowing developers to leverage server-side capabilities directly within their component tree. But how do these server-rendered components communicate with the client-side React runtime? The answer lies in the React Flight Protocol, a specialized wire format designed to transmit the serialized React element tree from the server to the client.
This post will dive deep into the React Flight Protocol, explaining its structure, how it handles various data types, and why it's a foundational technology for the future of web development.
1.Client-Side Rendering
For years, React applications predominantly embraced a client-side rendering (CSR) model, where the browser downloaded a large JavaScript bundle, hydrated the DOM, and managed all subsequent UI updates. While powerful, this approach often led to:
Large JavaScript bundles: Shipping UI logic, data fetching, and state management to the client.
Waterfall data fetching: Client components often had to fetch data sequentially, leading to slower perceived load times.
Complex server-side rendering (SSR) hydration: Reconciling server-generated HTML with client-side React.
React Server Components address these challenges by enabling developers to render components entirely on the server, keeping their code and data fetching logic off the client bundle. The magic that bridges the server and client in this new architecture is the React Flight Protocol.
2.What is React Flight Protocol?
At its core, the React Flight Protocol is a compact, streaming, JSON-like serialization format specifically designed to represent a React element tree. It's not HTML; it's a description of the UI, including:
React elements: The structure of your components (e.g.,
<div>,<MyComponent>).Props: Data passed to components.
Client Component references: Pointers to client-side code that needs to be loaded and rendered on the browser.
Server Action references: Pointers to server-side functions that can be invoked from the client.
Crucially, the Flight Protocol allows React to send only the necessary instructions to the client. Server-only code, sensitive data, and large dependencies stay on the server, resulting in significantly smaller client-side bundles and improved performance.
3.React Flight Protocol vs. React Server Components Protocol: A Clarification
It's common to hear both "React Flight Protocol" and "React Server Components Protocol" used interchangeably. To be precise:
React Server Components (RSC) is the feature that allows you to write components that render exclusively on the server, with zero client-side JavaScript.
The React Flight Protocol is the underlying technical specification and wire format that enables RSCs to function. It's the language the server and client speak to exchange UI updates.
Therefore, there isn't a separate, distinct "React Server Components Protocol." When people refer to it, they are almost certainly referring to the React Flight Protocol itself. The Flight Protocol is the protocol used by Server Components.
4.How React Flight Protocol Works Under the Hood
The Flight Protocol is a stream of instructions and data. Unlike a single HTTP response that delivers a complete HTML page or a JSON API payload, the Flight stream can deliver parts of the UI as they are ready, interweaving different types of information.
The Streamable, Interleaved Format
The protocol uses a custom, line-delimited format where each line typically starts with a single character indicating the type of instruction, followed by an ID and then the associated data. This allows the client to parse and process the stream incrementally.
Common instruction types include:
J: JSON data (e.g., props, element structures).M: Module reference (for Client Components).A: Asynchronous instruction (e.g., for Suspense boundaries).S: Symbol reference (e.g.,$$typeoffor React elements).R: Root element.
Serializing Components and Elements
When a Server Component renders, its JSX output is not converted to HTML. Instead, it's serialized into a JSON-like representation. For example, a simple <div>Hello</div> might be represented as ["$","div",null,{"children":"Hello"}].
The $ prefix is a convention for special React elements. $$typeof symbols, which React uses internally to distinguish different types of elements (like REACT_ELEMENT_TYPE, REACT_SERVER_COMPONENT_TYPE), are also serialized efficiently.
Client References (Module References)
This is where the magic of integrating server and client components happens. When a Server Component renders a Client Component, the client component's code is not sent over the Flight Protocol. Instead, the server sends a reference to the client component's module.
The M instruction is used for this. It maps an arbitrary ID to a specific client component module. The client-side React runtime then uses this ID to dynamically import the actual JavaScript module for that component.
The reference format often looks like ["$","M",<id>,null,<props>], where <id> refers to a module previously defined in the stream (e.g., M1: {"id":"./path/to/ClientComponent.js#default"}).
Data Serialization and Hydration
The Flight Protocol efficiently serializes various data types, including primitive values, arrays, and objects. It can also handle more complex types like Promises, allowing Suspense boundaries to work seamlessly across the server-client divide.
The client-side React runtime reads this stream, reconstructs the React element tree, and then renders it. When a client component is referenced, the runtime fetches its JavaScript bundle, instantiates it, and hydrates it with the props received from the server.
5.The Wire Format Structure (Payload Examples)
Let's illustrate with a simple example. Imagine a Server Component (Page) that renders a Client Component (ClientButton).
// app/page.tsx (Server Component)
import ClientButton from './ClientButton'; // This is a Client Component
export default function Page() {
const message = "Hello from Server!";
return (
<div>
<h1>{message}</h1>
<ClientButton text="Click me!" />
</div>
);
}
// app/ClientButton.tsx (Client Component)
'use client'; // This directive marks it as a Client Component
import { useState } from 'react';
export default function ClientButton({ text }: { text: string }) {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
{text} Count: {count}
</button>
);
}
When the Page Server Component is requested, the server might send a Flight Protocol payload similar to this (simplified and illustrative):
J0: ["$","div",null,{"children":[["$","h1",null,{"children":"Hello from Server!"}],["$","M",1,null,{"text":"Click me!"}]]}]
M1: {"id":"./app/ClientButton.tsx#default"}
Let's break this down:
J0: ...: This is a JSON instruction (J) with ID0. It describes the root React element structure.["$","div",null,...]: Represents adivelement."children": [...]: An array of children.["$","h1",null,{"children":"Hello from Server!"}]: Theh1element with its text.["$","M",1,null,{"text":"Click me!"}]: This is the crucial part. It's an instruction to render a module reference (M) with ID1. Thenullis for the key, and{"text":"Click me!"}are the props passed to theClientButton.
M1: {"id":"./app/ClientButton.tsx#default"}: This is a Module instruction (M) with ID1. It tells the client that whenever it encounters a reference to module1, it should import thedefaultexport from./app/ClientButton.tsx.
The client-side React runtime receives this stream. It sees the div and h1 elements and renders them. When it encounters ["$","M",1,...], it looks up module 1, dynamically imports ./app/ClientButton.tsx, and then renders ClientButton with the provided text prop. The ClientButton's interactivity (the useState hook) is handled purely on the client.
6.The React2Shell Security Vulnerability
While the Flight Protocol was designed to be a secure serialization format, December 2025 revealed a critical flaw in its implementation, now widely known as React2Shell. This vulnerability (CVE-2025-55182) carries a maximum severity score of CVSS 10.0, highlighting that even robust protocols can suffer from implementation defects.
What is React2Shell?
React2Shell is an Insecure Deserialization vulnerability inherent to the React Server Components (RSC) "Flight" protocol implementation itself (specifically in the react-server-dom-* packages). Unlike previously thought, this is not just about user data; the vulnerability lies in how the React server runtime deserializes the stream of component instructions.
How it Works
The Flight Protocol uses special prefixes (like $, @, and $F) to denote different data types (references, promises, symbols). The vulnerability exploits the reviveModel function—the internal mechanism React uses to reconstruct the component tree on the server.
The Attack: An attacker sends a maliciously crafted HTTP request containing a Flight stream with specific polluted prototypes or referencing internal gadgets (using the
$@chunk type).The Execution: Because the parser failed to properly validate these keys against the object's own properties, the server blindly deserializes the payload.
The Result: This triggers Remote Code Execution (RCE), allowing the attacker to run arbitrary shell commands on the server without any authentication.
The December 11, 2025 Update Following the initial disclosure, further scrutiny by the security community revealed two additional vulnerabilities in the same subsystem, addressed in the December 11th security patch:
CVE-2025-55184 (DoS): A high-severity flaw where crafted requests can trap the server in an infinite loop, causing a Denial of Service.
CVE-2025-55183 (Source Disclosure): A medium-severity issue that could trick the server into returning the compiled source code of Server Actions, potentially leaking business logic.
Mitigation and Best Practices The only effective mitigation for React2Shell is to patch immediately.
Upgrade Essential Packages: Ensure
nextis upgraded to 15.1.11+ or 14.2.35+ (for older versions). If you are using raw React 19, ensurereact-server-dom-webpackis version 19.0.1+.Audit Dependencies: Check for any third-party libraries or internal tools that might be bundling older versions of the RSC renderer.
Least Privilege: Ensure your Node.js server process runs with the absolute minimum permissions required, limiting the blast radius should an RCE occur.
Conclusion
The React Flight Protocol is a sophisticated yet elegant solution to the challenge of building modern, high-performance web applications with React. By defining a streamable, interleaved wire format, it enables Server Components to deliver significant performance benefits and a streamlined developer experience.
Read More about the React2Shell vulnerability
PoC by Lachlan for React2Shell Vulnerability: https://github.com/lachlan2k/React2Shell-CVE-2025-55182-original-poc




