skip to content

When a meta-framework streams a route's HTML, what goes out first and how does a slow region's markup reach its place later?

level: middleimportance: must knowfreq 62%

answer

  1. one response, several flushes
  2. placeholder first, markup later
  3. completion order, not document order
  4. appended at the end, then moved
  5. inline script performs the swap

basics

~20 s

The shell goes first: the document frame with a placeholder wherever a region is still pending. As each region resolves, its markup is appended to the same open response, with an inline script that moves it into place.

solid answer

~50 s

A streamed route response is one HTTP response sent in pieces. The first piece is the **shell** — the document frame, the `<head>`, and everything the levels above the slow parts could already render, with a placeholder element holding fallback markup wherever a region is still pending. The browser parses that immediately, starts fetching the linked CSS and JavaScript, and paints, while the connection stays open. As each pending region resolves, the server appends its finished markup near the end of the still-open body, usually inside a hidden container, followed by a few lines of inline script that move those nodes into the placeholder and drop the fallback. Regions therefore land in *completion* order rather than document order, and none of them costs an extra request — they ride the response the browser is already reading.

code

html · 14 lines
html
<!-- flushed first: the shell -->
<main>
  <h1>Product</h1>
  <section id="reviews"><p>Loading reviews…</p></section>
</main>

<!-- appended later, on the same open response -->
<div hidden id="chunk-1"><ul><li>Arrived quickly, well packed</li></ul></div>
<script>
  var slot = document.getElementById('reviews');
  var box = document.getElementById('chunk-1');
  slot.replaceWith(box.firstChild);
  box.remove();
</script>

go deeper

for a junior

Remember the two kinds of pieces: a shell with placeholders, then chunks that fill them. It is all one response, not a page plus follow-up requests.

for a middle

Be able to explain the relocation: markup is appended at the end of the open body and an inline script moves it into the placeholder, which is why arrival order is completion order.

for a senior

Show you know where the effect disappears in production — a buffering intermediary, a slow outer level that delays the shell, or a client without script that never runs the swap.

for a principal

Frame it as a delivery contract: streaming trades a predictable single payload for earlier paint and an early commit, and the team has to decide which routes can pay that price.

## What the shell is Streaming a route's HTML means the server writes one response in several flushes instead of building the whole document and sending it at the end. The first flush is the **shell**: the doctype, the `<head>`, the page frame, the navigation, and every region whose data was already in hand when the render reached it. Wherever the route declared a region that may resolve later, the shell carries a **placeholder** — a real element containing the fallback markup (a skeleton, a heading with no rows under it, a spinner) plus an identifier the server remembers. The browser treats that partial document exactly as it treats a slow download of a complete one: it parses as bytes arrive, discovers the stylesheet and script URLs in the head, starts those requests, and paints the frame. It does not know, and does not need to know, how much more is coming. ## How a late chunk finds its placeholder HTML is a single ordered byte stream, and bytes already flushed cannot be rewritten. A region that resolves after the shell has gone out therefore cannot be written where it belongs in the document. Most meta-frameworks solve it the same way — append and relocate: 1. the finished markup for that region is appended near the end of the still-open `<body>`, typically inside a hidden container so the parser has somewhere legal to put it; 2. a few lines of **inline script** follow it, find the placeholder by its identifier, move the finished nodes into that position (or replace the placeholder outright), and remove the now-empty container; 3. the script runs the instant the parser reaches it, so the swap happens as the bytes land rather than at load time. | piece | flushed when | what it carries | |---|---|---| | shell | the levels above the pending regions resolve | document frame, head, ready markup, placeholders with fallbacks | | region chunk | that region's data resolves | finished markup in a hidden container | | patch script | immediately after its chunk | the instruction that moves the markup into its placeholder | | close | the last pending region resolves or fails | the end of the body and the response | ## Arrival order is completion order The stream is ordered by when work finished, not by where the output belongs. Three pending regions — a sidebar, a table in the middle, a footer widget — appear in whatever order their data resolves, so the footer widget can fill in before the table above it. Users see the page settle in a slightly unfamiliar order; that is the price of not making the fastest region wait for the slowest. Placeholders can also nest: a pending region's own markup may contain another placeholder, which the framework resolves with a later chunk still. Frameworks differ in how much control they give over this — some let a group of regions be revealed in document order even when they finish out of order, others always reveal on completion. ## What it buys and what it costs - **No extra round trip.** The late regions travel on the response already in flight, so there is no second request and no client-side fetch waterfall to set up. - **The shell is only as early as its slowest ancestor.** Nothing flushes until every level above the placeholders has resolved, so one slow outer level delays the whole document no matter how many regions are deferred below it. - **Script is load-bearing for the swap.** A client that does not run script keeps the fallback markup on screen while the real content sits inert at the end of the document. Routes whose content must be in the served markup are better rendered without deferral. - **An intermediary that buffers erases the effect.** Anything between server and browser that collects the full body before forwarding turns the stream back into one delivery; the server still streamed, the user still waited. - **The response is committed early.** Status, headers and cookies leave with the shell, which is what makes deferring the wrong work a correctness problem rather than a performance one. ## What the browser is doing meanwhile Because the document is still open, the parser stays in a streaming state: scripts referenced in the head may already have executed, stylesheets may already be applied, and the page can become interactive before the last region has arrived. Work that takes over the server markup on the client generally runs per region as each one lands, rather than waiting for the document to end — which is why a streamed page can respond to input in its finished areas while other areas are still skeletons.

  • What does a client that never executes script end up seeing on such a page?
    The placeholders keep their fallback markup, and the real content sits inert in the hidden container at the end of the document, because the swap is a script step. Content that must exist in the served markup — for clients without script, or for consumers that only read HTML — should not be deferred on that route.
  • Can a deferred region contain another deferred region?
    Yes. The chunk that fills an outer placeholder can itself carry a placeholder, which a later chunk fills. Each level resolves independently, so a nested region does not hold up its parent's reveal, and the document can keep settling after the outer region has already appeared.
  • What happens if the connection drops while regions are still pending?
    The browser keeps the partially parsed document: everything already delivered stays on screen and the pending placeholders keep their fallbacks forever. There is no error page — the response had already reported success — so recovery has to be a client-side retry or a reload initiated by the user.

saying these in an interview costs you the question

  • Thinks the browser shows nothing until the full response arrives
  • Says late regions are revealed in document order
  • Assumes each deferred region costs an extra client request
  • Believes the server rewrites earlier bytes in place
  • Treats the fallback as client-only, not part of the served HTML