app/layout.js→ HTML + ❘❘❘ →self.__next_f.push(chunk)
RSC / Flight · first principles · the complete pipeline
contents

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.

▶ one response · visible HTML · the __next_f array · the browser paint
00 / 00
FRAME 00 · —

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.

visible HTML — what the browser paints Flight / RSC payload — the React model the __next_f array · server boundary a common misconception
the most important early correction Your layout is not converted into plain HTML and forgotten. Next produces two coordinated outputs: visible HTML for the first paint, and an embedded React / Flight model so the browser knows what React tree that HTML represents. The visible HTML is only one half of the result; the many self.__next_f.push(...) scripts are the other half.
2
coordinated outputs
1
HTTP response
0 KB
server components ship
10
mental models

01 At first-principles level

one source · two outputs · one response

One 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.

▶ layout.js → server module → Server Component → React tree → ( HTML + Flight ) → browser
00 / 00
FRAME 00 · —

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.

lock this in · HTML

The HTML is what the browser paints — immediate, non-interactive structure for the first frame.

lock this in · Flight

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 copied

Your 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."

your fileRootLayout({children}) → Next CALLS it during render → becomespart of a React element tree
— not "copied into an HTML file." The return value is rendered. —

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.

this line
console.log('layout rendered')

logs in the server terminal, not the browser console.

this line
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.

the <!-- --> marker That comment is a React server-rendering text separator. It helps React preserve exact text-node boundaries when reconciling the server-rendered DOM — so "Page rendered on" and the timestamp stay distinct nodes and adoption lines up without a hydration mismatch.

03 Filesystem → route tree

your folders are the routing DSL

Next 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.

▶ from files to rendered tree · step through the mapping
00 / 00
FRAME 00 · —

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.

files on disk
src/app
├─ layout.js      ← RootLayout
├─ styles.css     ← imported by RootLayout
└─ page.js        ← Home page for /
read as routing information
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 page

Your {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:

render tree
<html lang="en">
└─ <body>
   ├─ <main>
   │  └─ <h1>Hello Next!</h1>
   └─ <footer>
      └─ <p>Page rendered on …</p>
visible HTML
<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:

callRootLayout({ children: <Home/> }) → nested<html><body><Home/><footer/> → flat HTML<main><h1>Hello Next!</h1></main>

05 Start where you already are

the whole trick, demystified

You 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.

▶ an array, a push, a log — then the same thing, renamed
the reframe that unlocks the rest Stop reading 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 first

Reading (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.

⊞ tap a token · the self-creating, idempotent array guard

Read expanded, the compact line is just an existence guard followed by a push:

compact
(self.__next_f = self.__next_f || []).push([0]);
expanded
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.

▶ four scripts · one shared queue · the array is created once, reused forever
00 / 00
FRAME 00 · —

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.

why not just const __next_f = [] Because each inline <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-tuple

Each 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 tupletagwhat it tells the runtime
[0]0Bootstrap. Create the Flight response reader. No data — that is why the second slot is absent.
[1, "3:I[…]\n0:[…]"]1A chunk of the serialized React model: one or more newline-separated rows. By far the most common.
[2, …] / [3, …]2 · 3Other internal signals the runtime uses (e.g. form / error coordination). Rare; safe to treat as plumbing.
⊡ the wire, token by token · tap or hover anything to decode it
tap any token above

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.

why tag it instead of just pushing the string The leading number turns the array into a tiny instruction tape: the same 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 scripts

You 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.

▣ your layout (the small part) inside Next's document shell

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 response

The 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.

HTML answers

"What should the browser paint immediately?"

Flight payload answers

"What React tree produced this HTML, and how should the App Router continue from here?"

▦ the response tape · one stream, read top → bottom

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
the misconception to drop here The amber chunks are not a second copy of your HTML as a string. They are serialized React — a tree of elements, their props, and references to client components by module id. Same UI, two audiences: the HTML is for the browser's rasterizer; the Flight chunks are for React's reconciler. On initial load the payload arrives embedded in the HTML stream; later navigations can fetch only the RSC payload without a full HTML document.

10 How many push scripts?

the question everyone asks reading view-source

How 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.

▦ many scripts in one streamed response · count ≈ number of Flight chunks

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.

HTML

What the browser can immediately display. The rasterizer reads it and paints pixels — no JavaScript required.

__next_f.push(...)

Serialized React/Next routing + component-tree data the client runtime needs after the preview appears, to reconcile Server and Client Components.

the one-line answer A Next.js document can contain many 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.

▶ watch one document stream in · head → shell → tail → the swap
00 / 00
FRAME 00 · —

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.

the teleport — why a script's position in the file is misleading The resolved HTML arrives at the bottom of the stream, but it belongs in the middle of the page. $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.

▥ the four kinds of script in a Next.js document

① 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 rows

Crack 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.

⊞ tap a row · resolve the references

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:

tokenmeaning
Iimport / module reference
HLpreload / link / resource hint
$React element encoding
$L6reference to another record / module by ID
$Eencoded server-side function reference / special value
$Yplaceholder / internal unresolved or opaque value
the load-bearing idea A $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 only

Why 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
▶ peel the App Router boundaries · 7 layers from wrapper to your code
00 / 00
FRAME 00 · —

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 move

The 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.

▶ array as buffer → runtime drains it → push becomes a live pipe
00 / 00
FRAME 00 · —

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.

if you know streams, you already know this The array is a bounded buffer; the backlog is data produced before the consumer was ready; the runtime loading is the consumer attaching; replaying the backlog is the drain; overriding 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 → navigate

One 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.

▶ the full request timeline · 16 steps
00 / 00
FRAME 00 · —

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 line

Ten 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.

⊞ tap a model · ten lenses on the same line

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 detail

Every 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 · __next_f · push

self = the browser global object. __next_f = Next's Flight data queue. push = add this chunk to the queue.

the pipeline

RootLayout → server-rendered React tree → HTML + Flight payload → hydration / reconciliation → Flight-only navigation.

minimum viable path Short on time? 05 (push is just array push) → 06 (the || [] 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.
Back to JIT-Atlas