Skip to the solution
HardFrontend10 min read

What is the RSC payload and how does Next.js stream it to the browser?

701 views

The problem

Question presented to candidate: "When a user clicks a link in an App Router app, the network tab shows a request returning something that is not HTML and not JSON. What is that response, what is inside it, and how does the first page load get the same data?"

What a strong answer should cover:

  • The RSC payload is React's serialized output of the Server Component tree: rendered elements, references to Client Component modules, and the props passed to them.
  • It is a line-based stream of rows (id:value) that can reference rows not yet sent, which is how Suspense content arrives later in the same response.
  • On the first load it is inlined into the HTML as self.__next_f.push(...) scripts next to the streamed HTML; on client navigation it is fetched alone with the RSC: 1 header and text/x-component content type.
  • The _rsc query parameter and the Vary header keep caches from mixing HTML and RSC variants; Next.js 16 redirects requests with a wrong _rsc hash.
  • Consequences: everything passed to Client Components is visible in the payload, data is shipped twice on first load (HTML plus payload), and buffering proxies break streaming.

Clarifying questions expected:

  • "Is this about the first page load or client-side navigation?" β€” the transport is different.
  • "Is there a CDN or proxy in front?" β€” header forwarding and buffering decide whether it works.

Code / implementation expected: Optional β€” capturing the payload with curl -H 'RSC: 1' and reading a few rows.

  • nextjs
  • rsc
  • streaming
  • internals

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

10 min read Β· 9 sections

Read the code

The lab page: a Server Component, a Client Component and a slow Suspense child
// app/product/page.tsx
import { Suspense } from "react";
import { connection } from "next/server";
import LikeButton from "./LikeButton";

async function Reviews() {
  await new Promise((r) => setTimeout(r, 1500)); // a slow data source
  return (
    <ul>
      <li>Great kettle</li>
      <li>Boils fast</li>
    </ul>
  );
}

export default async function Product() {
  await connection(); // render per request so the stream is visible
  return (
    <main>
      <h1>Kettle</h1>
      {/* Client Component: the payload carries a module reference and the prop "initial":7 */}
      <LikeButton initial={7} />
      <Suspense fallback={<p>Loading reviews...</p>}>
        <Reviews />
      </Suspense>
    </main>
  );
}

// app/product/LikeButton.tsx
"use client";
import { useState } from "react";

export default function LikeButton({ initial }: { initial: number }) {
  const [n, setN] = useState(initial);
  return <button onClick={() => setN(n + 1)}>Like {n}</button>;
}
Capturing the payload and its timing yourself
# 1. The payload on its own, as client navigation requests it.
#    Without a matching _rsc value Next.js 16 answers 307; "?_rsc" with no value matches
#    a request that sends no router-state headers.
curl -s -D - -H 'RSC: 1' "http://localhost:3000/product?_rsc"

# 2. The same payload inlined in the HTML stream
curl -s http://localhost:3000/product | grep -o 'self.__next_f.push([^<]*' | head

# 3. When each chunk arrives (curl buffers, so read the body as a stream)
cat > observe.mjs <<'JS'
const t0 = Date.now();
const res = await fetch(process.argv[2], { headers: { rsc: "1", "accept-encoding": "identity" } });
const reader = res.body.getReader();
for (let i = 1; ; i++) {
  const { value, done } = await reader.read();
  if (done) break;
  console.log(`chunk ${i} +${Date.now() - t0} ms ${value.length} bytes`);
}
JS
node observe.mjs "http://localhost:3000/product?_rsc"

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 112 of 129 in Next.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Next.js questions
Up nextHard

What are Multi-Zones in Next.js and when would you split an app into zones?

Continue