00 Explorable explanation · the RSC / Flight pipeline, end to end
Your layout.js becomes two coordinated outputs — visible HTML for your eyes, and a growing array of serialized React the runtime replays.
A single Next.js App Router response is a pipeline made visible: your layout.js source becomes a server-rendered React tree, then HTML, then a Flight payload, then browser hydration. Open the page source and you find the strange shape: ordinary HTML you can see, interrupted again and again by <script>self.__next_f.push([…])</script>. self.__next_f is a plain array, and .push is the same Array.prototype.push you have called a thousand times. The server writes those pushes into the response as it streams; the array fills; and when React's runtime arrives it drains the array and replays it, reconstructing your component tree over the HTML the browser already painted. Press play and watch one whole request assemble itself.
Press play to stream one full response
Cyan is HTML the browser can paint. Amber is a Flight chunk — one self.__next_f.push(...). Violet is the array those chunks land in. Watch all three advance together.
self.__next_f.push(...) scripts are the other half.
01 At first-principles level
one source · two outputs · one responseOne source, two outputs, one response
At first-principles level, your app/layout.js is compiled, called as a Server Component, and its returned React element tree is rendered into two things that ship in a single HTML response. Press play to follow the source all the way to the browser.
Press play to follow one source to two outputs
Your file is compiled, invoked on the server, and its returned tree is rendered into both HTML and a Flight payload — delivered together in one response.
The HTML is what the browser paints — immediate, non-interactive structure for the first frame.
The Flight payload is what React/Next uses to remember, resume, reconcile, and navigate the app.
02 Server Component, not a template
your function is called, not copiedYour RootLayout is not an HTML template
function RootLayout({ children }) { console.log('layout rendered') return ( <html lang="en"> <body> {children} <footer> <p>Page rendered on {new Date().toLocaleString()}</p> </footer> </body> </html> ) }
This does not mean "Next copies this JSX into an HTML file." It means "Next will call this function during rendering, and the return value becomes part of a React tree."
In the App Router, app/layout.js is a special file. The top-most one is the root layout, and it must define the document-level <html> and <body> tags. Because you did not put "use client" at the top, this layout is a Server Component by default — layouts and pages render on the server, can be streamed, and only need Client Components when you require browser APIs, event handlers, state, or effects.
console.log('layout rendered')
logs in the server terminal, not the browser console.
new Date().toLocaleString()
runs during the server render — so the printed time is frozen into the HTML.
In your pasted HTML, that is why the final page contains Page rendered on <!-- -->6/22/2026, 9:53:29 PM.
03 Filesystem → route tree
your folders are the routing DSLNext first builds a route tree from your filesystem
Your files roughly imply a route tree. Next reads the folder structure as routing information — there is no route-config object; the folder tree itself is the routing language.
How a folder of files becomes a React tree
Step through it: the files you wrote, read as routing information, then assembled into the boundary-wrapped tree Next actually renders.
src/app ├─ layout.js ← RootLayout ├─ styles.css ← imported by RootLayout └─ page.js ← Home page for /
URL / └─ root segment "" ├─ layout: RootLayout └─ page: Home
So your final React tree is not just RootLayout. It is closer to RootLayout → children slot → LayoutRouter → Home page. That is why your output contains internal references you never wrote — they are App Router infrastructure that Next inserts around your route so it can support nested layouts, templates, loading states, errors, not-found pages, metadata, streaming, and client-side navigation:
layout-router.js
render-from-template-context.js
error-boundary.js
not-found-boundary.js
metadata-boundary.js
04 {children} → the rendered page
the slot fills with your pageYour {children} becomes the rendered page
Your layout says <body> {children} <footer>…</footer> </body>, and your page returns something like <main><h1>Hello Next!</h1></main>. So the render tree becomes a single nested structure:
<html lang="en"> └─ <body> ├─ <main> │ └─ <h1>Hello Next!</h1> └─ <footer> └─ <p>Page rendered on …</p>
<body> <main> <h1>Hello Next!</h1> </main> <footer> <p>Page rendered on <!-- -->6/22/2026, 9:53:29 PM</p> </footer> </body>
So the direct transformation is just a function call whose children argument is your page, collapsed into flat markup:
05 Start where you already are
the whole trick, demystifiedYou have written this exact code. That is the secret.
Before anything Flight-specific, run the most ordinary JavaScript there is. Make an empty array. Push a couple of values. Log it. Nothing here is framework magic — push appends to the end and returns the new length; the array just grows.
Now the reveal: flip the toggle. The Next.js version is the same widget. Same empty array, same .push, same growth. Only two things changed — the array has an unusual name (self.__next_f), and the values you push are tagged tuples of serialized React instead of greetings.
self.__next_f.push(...) as a Next.js incantation and start reading it as "append a chunk to an array." Who appends? The server, by streaming <script> tags. What gets appended? Pieces of your UI, encoded as data. The rest is just format and timing.
06 Anatomy · the very first script
why this odd little line comes firstReading (self.__next_f = self.__next_f || []).push([0]) token by token.
The first Flight script on the page is always a bootstrap. It looks cryptic because it does two jobs at once: make sure the array exists, then push the first instruction into it. Tap each part below — the only genuinely interesting piece is || [], the guard that makes this line safe to run more than once and in any order.
Read expanded, the compact line is just an existence guard followed by a push:
(self.__next_f = self.__next_f || []).push([0]);
if (!self.__next_f) { self.__next_f = [] } self.__next_f.push([0])
Why guard with || [] at all? Because a streamed page is a pile of independent <script> tags — and a route can emit many of them. Any one might be the first to run, and every one after needs the array the first created. Watch four scripts arrive: the guard fires once, then every later script reuses the same queue.
Press play to see the guard in action
The center slot is the global self.__next_f. Watch how || [] creates it the first time and is harmlessly skipped every time after.
<script> tag is its own scope. A const would be invisible to the next script. By putting the queue on the global object, every script can say self.__next_f.push(...) and they are all pushing into the same shared place. It is the JSONP-flavoured equivalent of window.dataLayer = window.dataLayer || [].
07 The format · what gets pushed
every push is a 2-tupleEach item is [type, data] — a small tagged envelope.
The values flowing into the array are not bare strings. Each one is a two-element tuple: a number tag that says what kind of instruction this is, followed by its payload. The bootstrap pushes [0] — tag 0, no payload, meaning "initialize the reader." Almost everything after is [1, "…"] — tag 1, a string payload, meaning "here is more of the serialized model."
| pushed tuple | tag | what it tells the runtime |
|---|---|---|
[0] | 0 | Bootstrap. Create the Flight response reader. No data — that is why the second slot is absent. |
[1, "3:I[…]\n0:[…]"] | 1 | A chunk of the serialized React model: one or more newline-separated rows. By far the most common. |
[2, …] / [3, …] | 2 · 3 | Other internal signals the runtime uses (e.g. form / error coordination). Rare; safe to treat as plumbing. |
Every part of self.__next_f — the symbols, the digits, the escapes, the sigils — explained. Hover or tap a highlighted token and its meaning appears here; matching tokens light up together.
push channel can say "boot up," "here's data," or "here's a control signal," and the runtime dispatches on the tag. It is the difference between a pipe and a protocol.
08 The document shell Next adds
head · metadata · CSS · framework scriptsYou wrote <html><body> — Next wrapped it in a full document.
Your source only wrote <html lang="en"><body>…</body></html>. But the output has a <head> you never authored, a <link> you never wrote, and framework <script>s. They all come from Next's document/metadata system wrapping your layout.
Why did Next add <head> if you did not write one?
In the App Router, metadata is resolved on the server so it can be included in the initial HTML response. Even when you do not export custom metadata, Next still emits essential defaults like charset and viewport. The metadata system is why your Flight payload includes entries such as MetadataBoundary, ViewportBoundary, __next_metadata_boundary__, and __next_viewport_boundary__ — which eventually become the <meta charset> and <meta name="viewport"> tags in the head.
Why did your CSS import become a <link> tag?
You wrote import "./styles.css"; Next sees that import in the root layout and treats it as a global app-level stylesheet. It is bundled and extracted to /_next/static/css/app/layout.css, then linked in <head> as a normal stylesheet the browser downloads. The ?v=1782158009762 suffix is a development-mode cache-busting timestamp that helps the browser get the newest stylesheet during dev.
<link rel="stylesheet" href="/_next/static/css/app/layout.css?v=1782158009762" data-precedence="next_static/css/app/layout.css" />
Why are there framework scripts?
Your output also has scripts that are not from your layout — they are the Next / React client runtime. Even though your RootLayout is a Server Component, the browser still needs some JavaScript so Next can hydrate Client Components, manage the App Router, process future navigations, and interpret the embedded RSC payload.
09 The braid · why it's woven, not stacked
HTML and Flight, alternating down one responseThe response is braided: paintable markup and data chunks, in turns.
On the server, Server Components render into a special RSC payload, and that payload plus Client Component instructions are used to prerender HTML. So your response is not just HTML — it is an HTML shell + the RSC/Flight payload + router bootstrap data + client runtime scripts, interleaved on purpose. Read the raw bytes top to bottom and you see the rhythm: an HTML shell, then visible markup, then a Flight chunk, then more HTML, then another chunk, then a run of chunks.
"What should the browser paint immediately?"
"What React tree produced this HTML, and how should the App Router continue from here?"
Streaming the two together is what lets the page show pixels before the response finishes. As soon as the browser reaches a script, it runs it and stores the chunk — so it never waits for one giant JSON file at the end:
Incoming HTML stream
│
▼
Browser parser
├─ sees <main><h1>Hello Next!</h1></main>
│ ↓ paints visible page
├─ sees <script>self.__next_f.push(...)</script>
│ ↓ executes immediately → stores chunk in __next_f
└─ continues parsing
10 How many push scripts?
the question everyone asks reading view-sourceHow many self.__next_f.push(...) scripts does a page have?
You will see anywhere from a couple to dozens. There is no fixed number — and crucially, it is not one per page, one per component, or one per Server Component. The count tracks something else entirely: how many Flight / RSC payload chunks Next decides to flush into this particular response.
Why more than one at all? Because the initial HTML response is streamed. Next does not wait for the whole route to finish before sending bytes — it sends the visible HTML preview early, then appends pieces of the RSC payload as they become available, each as its own inline <script> push. That is what lets a usable page paint while the hidden Flight data is still arriving alongside it.
what pushes the count up
a larger route emits more chunks when the payload carries more: • route tree information • Suspense / streaming boundary results • layout + page segment data • metadata / <head> information • Server Component render results • CSS / font / module preload hints • Client Component placeholders • props passed Server → Client • references to client JS chunks • streamed async data results
So the precise statement: the number of push scripts is implementation- and route-dependent, with no public fixed maximum. It rises and falls with payload size, how many route segments and Suspense boundaries are involved, how much Server Component output exists, how many Client Component references are needed, how Next chooses to split and flush the stream, and even whether you are in dev or production and which Next version you are on.
What the browser can immediately display. The rasterizer reads it and paints pixels — no JavaScript required.
Serialized React/Next routing + component-tree data the client runtime needs after the preview appears, to reconcile Server and Client Components.
self.__next_f.push(...) scripts. They exist because the RSC / Flight payload is streamed into the same initial HTML response in chunks — and self.__next_f is the browser-side queue every one of them appends to, which the runtime later drains to rebuild the App Router tree.
Where the scripts sit — and why there
They are not sprinkled at random. The response has three zones, and each script lands in the zone decided by when its data becomes available — which is exactly why the order you read on screen does not match the order in the file.
Press play to stream the document in
The left is the bytes arriving in order; the right is what the browser paints. Watch the head arrive, the shell paint with a skeleton, then the tail teleport real content into place.
① The <head>, in the very first chunk. Before a single component has finished, Next emits the resource hints and the <script async src> bundle tags. The browser sees them immediately and starts downloading JavaScript, CSS, and fonts while the server is still rendering — fetching happens during server think-time, not after it.
② The <body> shell paints next. The static markup streams out, and every Suspense boundary is written as a fallback wrapped in comment markers: <!--$?--> (pending), an empty <template id="B:0"> slot, the skeleton UI, then <!--/$-->. The slot is a placeholder the browser can already paint around.
③ The streamed tail, in resolve order. When a boundary finishes on the server, three things are appended near the end of the body — wherever the byte stream happens to be: the resolved markup inside a <div hidden id="S:0">, a $RC("B:0","S:0") swap script, and a self.__next_f.push(...) carrying that boundary's RSC payload.
$RC — React's completeBoundary helper — is the fix: it lifts the hidden S:0 content out of the tail and drops it into the B:0 slot, deleting the fallback. That is why a fast boundary can appear before the JS bundle even loads: the swap is a tiny inline script, not part of hydration. And they are inline (not a second request) for one reason — it is the same streamed response, so each chunk runs the instant the parser reaches it, filling self.__next_f in arrival order with no round-trip.
Every script on the page, and why it exists
Past the __next_f.push lines, a Next.js document carries a few other <script> and <link> tags. Here is the whole cast, in four groups — the two that sit in the head, and the two that stream through the body.
① Resource hints are <link> tags, not scripts, but they drive script loading: preload and modulepreload tell the browser to start fetching a bundle before the tag that uses it appears; the stylesheet and font preloads do the same for CSS and fonts. They exist to overlap network time with server-render time.
② Bundle scripts are the real JavaScript, loaded with async. webpack-* is the module runtime; main-app-* boots React and the App Router client; app-pages-internals-* is the shared App Router runtime; the per-segment chunks (app/layout, app/page, and any shared [id] chunks) contain only your "use client" components; polyfills-* backfills older browsers and is skipped by modern ones. Together they hydrate the server-rendered HTML.
③ Flight data is the self.__next_f family you have been tracing: the bootstrap [0] that creates the queue, then [1, "…"] rows carrying the serialized element tree, props, and client-component references. Types 2 / 3 appear rarely, for internals like Server-Action metadata.
④ React streaming scripts are the DOM-surgery helpers: $RC swaps a resolved Suspense boundary into place, $RS moves a completed segment, and their function bodies are injected once the first time they are needed, then reused by id. These are React's, not Next's — Next layers the __next_f payload alongside them.
11 Zoom in · decoding the payload
the string is rows, and rows reference rowsCrack open a chunk: it's React, written as a numbered tape.
The string inside a [1, "…"] push is a sequence of rows separated by newlines. Each row is id:tag payload. Some rows are module references (a pointer to a client component's chunk — never the component's code), some are React elements written as arrays, and elements can point at other rows with a $L<id> reference. Tap a row to decode it and watch its references light up.
the records in your actual payload
Mapped onto a real response, the records decode like this — a mix of rendered Server Components, serialized elements, module references, a CSS hint, and metadata:
8:{"name":"RootLayout","env":"Server", ...} // a Server Component named RootLayout was rendered 5:["$","html",null,{"lang":"en","children":["$","body",null,{...}]}] // serialized element model for your layout c:{"name":"Home","env":"Server", ...} // your Home page was rendered on the server b:["$","main",null,{"children":["$","h1",null,{"children":"Hello Next!"}]}] // your page output: <main><h1>Hello Next!</h1> 6:I["...layout-router.js",["app-pages-internals",...],""] // an importable client module: Next's LayoutRouter 3:HL["/_next/static/css/app/layout.css?v=...","style"] // a resource hint / preload for your CSS 1c:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] 1a:[["$","meta","0",{"charSet":"utf-8"}]] // the metadata that becomes <meta> tags in <head>
the wire tokens
The exact tokens like I, HL, D, $L6, $Y, $E(...) are React/Next implementation details, not public API. Conceptually:
| token | meaning |
|---|---|
I | import / module reference |
HL | preload / link / resource hint |
$ | React element encoding |
$L6 | reference to another record / module by ID |
$E | encoded server-side function reference / special value |
$Y | placeholder / internal unresolved or opaque value |
$L3 inside an element is not the component — it is a reference to row 3, which is a reference to a module. This is how a Server Component tree can mention a Client Component without shipping its code inline: it ships a pointer, and the runtime fetches the real chunk only when needed. Server components contribute structure; client components contribute an address. Do not build application logic around these exact tokens — they can change between React/Next versions.
12 The machinery you render inside
you authored the clean part onlyWhy the payload includes things you did not write.
You see records for boundaries and roots you never authored — because your app is not rendered as only RootLayout → Page. It is rendered inside Next's App Router machinery: the boundaries that make nested routing, streaming, errors, metadata, prefetching, and client navigation work.
NotFoundBoundary OutletBoundary MetadataBoundary ViewportBoundary
GlobalError ClientPageRoot ClientSegmentRoot
Step through the layers wrapped around your component
Hover any box to highlight it, or step through outside-in: the server wrapper, your layout, the routers, the safety nets, the head, and finally the one box you wrote.
So your real server render is closer to this — you only authored RootLayout, Home, and styles.css; Next wraps it in everything else:
<AppRouterServerRender> <RootLayout> <body> <LayoutRouter parallelRouterKey="children"> <TemplateContext> <ErrorBoundary> <NotFoundBoundary> <MetadataBoundary> <Home /> ← the part you wrote </MetadataBoundary> </NotFoundBoundary> </ErrorBoundary> </TemplateContext> </LayoutRouter> <footer /> </body> </RootLayout>
13 The handoff · buffer becomes a channel
the one genuinely clever moveThe runtime drains the backlog, then replaces push.
At first, self.__next_f behaves like a normal array — chunks pile up because nobody is consuming them yet. Then Next's client runtime loads. Conceptually it reads what is buffered, then takes over push so later chunks are processed immediately instead of merely stored:
for (const chunk of self.__next_f) { processFlightChunk(chunk) } // …then keep consuming later chunks as they arrive
before runtime is ready
__next_f is a buffer — early chunks are stored.
after runtime is ready
__next_f becomes a delivery channel — later chunks are consumed.
Press play to watch the drain and the swap
The array is a buffer holding chunks until a consumer exists. The runtime is that consumer: it empties the backlog, then rewires push so later chunks skip the buffer entirely.
push is swapping a buffered queue for a live controller.enqueue(). One pattern, wearing a Next.js costume.
14 The full request timeline
request → render → stream → paint → hydrate → navigateOne request, sixteen steps, start to finish.
Every stage of the pipeline in order — from the browser asking for / to the app becoming interactive and future navigations going Flight-only. Press play, or scrub to any step. Color marks whose turn it is: server, the React/Flight model, paintable HTML, or the browser runtime.
Press play to walk the whole request
Sixteen steps from request to interactive. Each one highlights on the timeline with a short description of what is happening and who is doing it.
15 A diagram model of all this
ten angles on one lineTen models of self.__next_f.push(...)
In your HTML, Next uses this pattern many times to stream React/Flight chunks into the browser. Here it is from ten angles — tap through them. Each is a different lens on the same array.
HTML gives the browser something to paint. __next_f gives React/Next the serialized app model needed to continue, reconcile, hydrate, and navigate.
16 The glossary, as cards
tap any piece for the detailEvery token in self.__next_f.push([1, …]), named.
You now have the whole shape. These cards pin down each part precisely — what it is, why it is there, and the runtime-level or React-level fact underneath. Tap to open.
And the myths it dispels
The pattern is most misunderstood in three specific ways. Each card states the wrong model and the right one.
17 The whole thing, in one breath
The HTML response is a self-assembling program: paintable markup for your eyes, plus an array of serialized React the runtime replays to make those pixels interactive.
Your RootLayout becomes the final HTML because Next calls it as a Server Component and turns its returned React element tree into server-rendered markup. But Next also embeds the Flight/RSC payload because HTML alone is not enough for the App Router — the browser also needs the serialized React tree, router state, module references, metadata boundaries, and Client Component references so React can reconcile the page and Next can perform client-side navigation.
Read as one line: self.__next_f.push(chunk) is array.push(chunk). The array is self.__next_f — a get-or-created queue, guarded by || [] so any script can be first. Each pushed item is [tag, data]: [0] boots the reader, [1, "…"] delivers rows of the model. The server braids those <script> pushes between chunks of visible HTML so the browser paints early. And when React's runtime loads, it drains the backlog and rewires push into a live pipe — so the stream that started as bytes ends as your running app.
self = the browser global object. __next_f = Next's Flight data queue. push = add this chunk to the queue.
RootLayout → server-rendered React tree → HTML + Flight payload → hydration / reconciliation → Flight-only navigation.
|| [] guard) → 07 (the [0]/[1,data] tags) → 13 (the drain-and-swap handoff). That sequence is the 20% that makes every Next.js page source readable.