skip to content

A server-rendered page streams its HTML and paints in about 0.8 s, but clicking anything does nothing for another two seconds while the page's JavaScript boots up and attaches behaviour to the existing markup. What is happening in that gap, and what does hydrating progressively change about it?

level: seniorimportance: should knowfreq 36%

answer

  1. painted but not listening
  2. main thread is busy, clicks queue
  3. islands, not one big boot
  4. hydrate on visible or on first touch
  5. replay the click that triggered it

basics

~20 s

The gap is client-side JavaScript downloading, parsing and executing to attach behaviour to the server-rendered markup — work that occupies the main thread, so clicks queue. Hydrating progressively splits that work per region and defers most of it until each region is needed.

solid answer

~60 s

The paint is cheap because the markup arrived ready-made; interactivity is expensive because the client still has to load the whole bundle and walk the tree attaching listeners and state. That work runs on the main thread as a few long tasks, so input during it is queued rather than dropped — the page looks finished and behaves as if frozen, which is the worst combination for user trust. Progressive hydration attacks the two multipliers. First, scope: rather than booting the entire page, treat interactive regions as separate units and hydrate each on its own trigger — immediately for the search box, on visibility for a carousel below the fold, on first interaction for a rarely used dialog. Second, granularity: the bundle is split per region so a deferred region's code is not even downloaded. The result is not less total work if every region is eventually used, but far less work before the page responds, and it is broken into pieces short enough that input gets a turn between them.

go deeper

for a junior

Know that server-rendered markup paints before the JavaScript has run, and that the page cannot respond to clicks until that code has downloaded and executed.

for a middle

Explain that hydration is main-thread work, so input queues behind it, and describe splitting the page into independently hydrating regions with triggers such as visibility or first interaction.

for a senior

Show that you would measure responsiveness to real interactions rather than a lab score, choose region boundaries around shared state, and handle the replay of the event that triggered a deferred region.

for a principal

Own the architectural call: how much of the product should be interactive at all, whether an island-style split is worth its complexity for this codebase, and how to keep teams from shipping client code for regions that were never interactive.

## What the gap actually is Server-rendered HTML gives the browser something to paint without running any application code. That is why the paint is fast. But the markup is inert: buttons have no click handlers, inputs have no state, and none of the application's data structures exist on the client. Making the delivered DOM interactive — downloading the client bundle, parsing and compiling it, executing it, and walking the existing tree to attach listeners and reconstruct state — is what fills the two seconds. The crucial property is that this work is synchronous JavaScript on the main thread. The main thread is also the only thread that runs event handlers and produces frames. So while it is busy, a click is not lost — it is queued, and it is dispatched only when the thread is free. From the user's side the page looks completely finished and simply ignores them, then possibly does everything at once. That combination erodes trust faster than a slow page that visibly looks unfinished. ## Why a fast paint can make it worse There is a genuine tension here. Streaming and server rendering optimise for showing the page early; the earlier the page looks complete, the earlier the user tries to use it, and the wider the window in which their input goes nowhere. A page that painted at 2.4 s and became interactive at 2.5 s can feel better than one that paints at 0.8 s and responds at 2.8 s. The fix is not to paint later — it is to close the gap. ## The two levers **Scope: hydrate less.** Most of a page is not interactive. Static text, headings, images, footers and marketing copy need no client code at all; they were correct the moment they were parsed. If the hydration unit is the whole page, all of that inert content is walked and re-processed anyway. If the unit is a region — the search box, the cart widget, the comment form — then only those regions cost anything, and the rest of the page is simply HTML. This is the idea behind island-style architectures: the page is static by default, with independently hydrating interactive regions. **Timing: hydrate later.** Regions do not all need to be live at the same moment. Useful triggers, in rough order of aggressiveness: - **Immediately** — the primary interaction of the page, above the fold. - **When visible** — anything below the fold, using a viewport observer to trigger hydration as it approaches the screen. - **On first interaction** — attach a cheap capturing listener that boots the region on the first pointer or keyboard event, then replays that event once the real handler exists. This costs the user a small delay on their first click but nothing at all if they never touch it. - **When the main thread is idle** — for regions that are neither critical nor tied to a trigger, so they are ready before the user asks but never compete with startup. The replay detail matters: without it, the click that triggered hydration is swallowed and the user has to click twice, which reads as a bug. ## What this buys, and what it does not Progressive hydration does not reduce total work if the user eventually engages with everything — it moves it. What it reliably reduces is the work that must complete before the page responds, and it breaks that work into shorter units so the main thread yields between them and queued input gets serviced. It also unlocks a real reduction, because a per-region split means the deferred region's code need not be downloaded at all until its trigger fires; users who never open the dialog never pay for it. ## The tradeoffs an interviewer wants to hear - **Complexity.** Per-region boundaries, triggers and code splits are more moving parts than one boot call, and they can be got wrong in ways that only show under slow networks. - **Cross-region state.** Regions that share state are awkward to hydrate independently; deciding the boundaries is a design exercise, not a mechanical one. - **First-interaction latency.** Deferring to interaction shifts cost onto the user's first click. For a primary control that is a bad trade; for a share menu it is free. - **Measurement.** Judge it by responsiveness to real interactions in field data, not by a lab score. A page can look better on a synthetic load test and still make the first click feel slow. ## Before reaching for it The cheapest wins usually come first: ship less client code, remove interactive behaviour from regions that never needed it, and cut third-party scripts competing for the same thread. Progressive hydration is a scheduling strategy layered on top of a bundle that has already been made as small as the product allows — not a substitute for that work.

  • If a click during that gap is queued rather than dropped, why do users say the page ignored them?
    Because the response arrives long after the intent. The handler eventually runs, but by then the user has clicked again or concluded the control is broken. Perceived responsiveness is about the delay between action and visible feedback, so a correct action delivered two seconds late is experienced as a failure, and duplicate submissions are a common side effect.
  • What breaks if you hydrate a region on first interaction without replaying that event?
    The event that triggered hydration is consumed before any real handler exists, so the user's first click does nothing and they have to click again. The pattern needs a capturing listener that records the event and re-dispatches it once the region is live — otherwise the optimisation reads as a bug on exactly the interaction the user cared about.
  • Which regions are poor candidates for deferred hydration?
    Anything the user is likely to touch first: the primary call to action, the search field, the login form, and any control above the fold. Deferring those trades a page-load cost for a first-click delay on the interaction that matters most. Regions that share mutable state with others are also poor candidates, because independent boundaries become hard to reason about.

The set is fully built and lit, but the actors are still backstage getting into costume. The audience sees a finished stage and cannot understand why nothing responds.

saying these in an interview costs you the question

  • Says input during hydration is dropped rather than queued
  • Thinks server rendering alone makes a page interactive
  • Defers the page's primary control to first interaction
  • Forgets to replay the event that triggered hydration
  • Treats hydration cost as unrelated to bundle size

context