# 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:

1. **React elements**: The structure of your components (e.g., `<div>`, `<MyComponent>`).
    
2. **Props**: Data passed to components.
    
3. **Client Component references**: Pointers to client-side code that needs to be loaded and rendered on the browser.
    
4. **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., `$$typeof` for 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`).

```typescript
// 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)**:

```typescript
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:

1. `J0: ...`: This is a JSON instruction (`J`) with ID `0`. It describes the root React element structure.
    
    * `["$","div",null,...]`: Represents a `div` element.
        
    * `"children": [...]`: An array of children.
        
    * `["$","h1",null,{"children":"Hello from Server!"}]`: The `h1` element 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 ID `1`. The `null` is for the key, and `{"text":"Click me!"}` are the props passed to the `ClientButton`.
        
2. `M1: {"id":"./app/ClientButton.tsx#default"}`: This is a Module instruction (`M`) with ID `1`. It tells the client that whenever it encounters a reference to module `1`, it should import the `default` export 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.

1. **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).
    
2. **The Execution:** Because the parser failed to properly validate these keys against the object's own properties, the server blindly deserializes the payload.
    
3. **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 `next` is upgraded to **15.1.11+** or **14.2.35+** (for older versions). If you are using raw React 19, ensure `react-server-dom-webpack` is 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

1. [https://nextjs.org/blog/security-update-2025-12-11](https://nextjs.org/blog/security-update-2025-12-11)
    
2. [https://aws.amazon.com/blogs/security/china-nexus-cyber-threat-groups-rapidly-exploit-react2shell-vulnerability-cve-2025-55182/](https://aws.amazon.com/blogs/security/china-nexus-cyber-threat-groups-rapidly-exploit-react2shell-vulnerability-cve-2025-55182/)
    
3. [https://blog.cloudflare.com/react2shell-rsc-vulnerabilities-exploitation-threat-brief/](https://blog.cloudflare.com/react2shell-rsc-vulnerabilities-exploitation-threat-brief/)
    

PoC by Lachlan for React2Shell Vulnerability: [https://github.com/lachlan2k/React2Shell-CVE-2025-55182-original-poc](https://github.com/lachlan2k/React2Shell-CVE-2025-55182-original-poc)
