Resumability in Qwik

WebQwikPerformanceJavaScript
Share on LinkedIn Share on X Share on Reddit Share on HN Share on Bluesky

Traditional React apps ship HTML that is inert until the browser downloads the full bundle, reconstructs the virtual DOM, and runs hydration — often 200KB or more of JavaScript before a single button works. Qwik's bet is that most of that work is redundant: the server already rendered the page and already knows which event handlers exist. Resumability means sending that knowledge forward in the HTML so the client picks up interactivity like resuming a paused process, not rebooting the machine.

Hydration replays what the server already did

In frameworks with hydration, the server renders HTML, then the client loads framework code and walks the entire component tree to attach listeners and rebuild state. Users on slow networks stare at a static page while JavaScript downloads. Worse, hydration cost scales with page size even when most components never receive a click.

Resumability inverts the contract: the server embeds enough information in the HTML for the client to attach behavior lazily, component by component, only when needed.

Fine-grained lazy loading with $ boundaries

Qwik splits code at event-handler boundaries. The optimizer transforms functions marked with $ into separately loadable chunks:

import { component$, useSignal } from '@builder.io/qwik';

export const Counter = component$(() => {
  const count = useSignal(0);

  return (
    <button onClick$={() => count.value++}>
      Count: {count.value}
    </button>
  );
});

onClick$ tells the optimizer to extract the handler into its own chunk. On first render, the button appears in HTML immediately. When the user clicks, Qwik fetches only the handler chunk — not the entire application bundle — and executes it. Each interactive element can have its own lazy boundary.

Serialized state in the HTML

Qwik serializes reactive state into the DOM as q:container and q:state attributes. When the client resumes, it reads those attributes instead of re-running component setup:

<button
  on:click="/build/chunk-abc.js#Counter_onClick"
  q:id="0"
  q:state="{\"count\":0}"
>
  Count: 0
</button>

The client knows the current count is 0 and knows exactly which file contains the click handler. No constructor runs. No effect hooks fire on load. State transfers from server to client like a serialized checkpoint.

The optimizer's role at build time

You do not manually create hundreds of lazy import paths. The Qwik optimizer (a Vite or Rollup plugin) scans for $ suffixes, extracts closures, generates chunk manifests, and wires event bindings in the SSR output. This is what separates Qwik from hand-rolled lazy hydration experiments — the splitting is automatic and consistent.

Build output includes a q-manifest.json mapping symbols to chunk URLs. The client runtime fetches chunks on demand using that manifest.

Performance implications in practice

Resumability targets three metrics: Time to Interactive, Total Blocking Time, and JavaScript bytes on initial load. Benchmarks on content-heavy pages often show near-zero JS until interaction because only the Qwik core runtime loads initially — typically a few kilobytes.

Tradeoffs exist. Highly interactive dashboards where every pixel responds to input may load many chunks quickly anyway, reducing the advantage. Content sites, marketing pages, and e-commerce product pages — where most users scroll and only some click — benefit most.

Mental model for developers coming from React

Concept React (SSR + hydration) Qwik (resumability)
Initial JS Full app bundle Core runtime only
State setup Client re-executes components Read from HTML
Event handlers Attached during hydration Lazy-loaded per handler
Lazy boundaries Route or component level Event handler level

Qwik components look familiar — JSX, hooks-like primitives (useSignal, useTask$) — but the $ suffix rules are non-negotiable. Functions crossing the server-client boundary must be serializable and extracted by the optimizer.

When resumability is worth the switch

Choose Qwik when initial load performance is a measured bottleneck, your pages are content-first with selective interactivity, and your team accepts a smaller ecosystem than React. The resumability model is architectural — it is not a drop-in performance patch for an existing React codebase.

Greenfield projects and performance-critical public pages are the sweet spot. Internal admin tools with constant interaction may not justify the paradigm shift.

Measuring resumability wins

Compare before/after on real deployments, not lab Lighthouse only:

If TTI improves but interaction latency regresses, your lazy boundaries may be too granular — batch related interactivity into single QRL chunks.

Prefetching handler chunks on hover

Qwik can prefetch onClick$ chunks on mouseover for perceived instant interaction — trades bandwidth for snappiness. Measure bytes prefetched vs click-through rate; prefetch everything and mobile data users suffer.

Resumability with auth-gated routes

Serialized state must not embed secrets — tokens belong in HttpOnly cookies read after resume, not in q:state. Security review resumable pages like SSR pages; exposed state in HTML view-source is visible to extensions.

Streaming SSR with resumability

Streaming HTML must still emit valid q:container boundaries — partial chunks breaking mid-attribute corrupt resume. Test streaming routes with Playwright interrupting network mid-stream.

Comparison table for stakeholders

Approach Initial JS Time to interactive on click
Full hydration High After full bundle
Islands Medium After island bundle
Resumability Low After handler chunk

Use table in architecture docs when PM asks why Qwik over Next for marketing site with few interactions.

Production rollout notes

Evaluate Qwik City routing if considering framework adoption — resumability benefits matter most on content-heavy routes with sparse interaction. Admin dashboards with constant form interaction may see less relative win; profile your route mix in Chrome User Timing before migration pitch to leadership.

Migration from React incremental

Migrate route-by-route — hybrid app with React root and Qwik islands possible during transition. Resumability wins on marketing pages first; keep complex React admin until Qwik City patterns proven for your auth and data layers.

Closing operational guidance

Measure bundle bytes at first interaction not first load — resumability KPI is handler chunk size on first click target, often primary CTA in hero. Ship changes behind feature flags, measure before and after on real traffic, and keep rollback one deploy revert away. Ship changes behind feature flags, measure before and after on real traffic, and keep rollback one deploy revert away. Ship changes behind feature flags, measure before and after on real traffic, and keep rollback one deploy revert away. Ship changes behind feature flags, measure before and after on real traffic, and keep rollback one deploy revert away. Ship changes behind feature flags, measure before and after on real traffic, and keep rollback one deploy revert away. Ship changes behind feature flags, measure before and after on real traffic, and keep rollback one deploy revert away. Ship changes behind feature flags, measure before and after on real traffic, and keep rollback one deploy revert away. Ship changes behind feature flags, measure before and after on real traffic, and keep rollback one deploy revert away. Ship changes behind feature flags, measure before and after on real traffic, and keep rollback one deploy revert away. Ship changes behind feature flags, measure before and after on real traffic, and keep rollback one deploy revert away.

Resources

Frequently asked questions

How is resumability different from partial hydration?

Partial hydration still downloads and parses component frameworks for hydrated islands, then executes their setup code on the client. Resumability serializes component state and event handlers into the HTML so the client resumes exactly where the server left off without re-executing component constructors. The difference is whether the client replays initialization or continues from a checkpoint.

Does Qwik eliminate all client-side JavaScript?

No. Qwik delays and splits JavaScript delivery rather than removing it entirely. Interactive features still need client code, but only the code required for the user's next action loads. Static content ships as HTML with near-zero JS until the user interacts with a specific component.

What is the Qwik optimizer and why does it matter?

The optimizer is a build-time tool that splits your application into tiny lazy-loadable chunks and generates `$` signals linking event handlers to their code. Without it, you would manually manage imports and lazy boundaries. The optimizer is what makes fine-grained resumability practical at scale rather than a hand-rolled experiment.

Hiring a senior Android / Flutter engineer?

I architect and ship production mobile software — Kotlin, Jetpack Compose, Flutter — for robotics, EV infrastructure, fintech, and real-time systems. Open to remote roles in Europe and the US.

Get in touch →