Summary
Resumability is Qwik's alternative to hydration. The server serializes component state, subscriptions, and event-listener references into the HTML; the browser picks up exactly where the server left off. No JS executes on load — only the handler for the event the user actually triggers is downloaded.
Jump to the interview angleResume-on-interaction flow
- 1
SSR: serialize everything
Qwik's server renderer writes application state and component subscriptions into a
<script type="qwik/json">block, and encodes each event listener as a DOM attribute:on:click="./counter-abc123.js#Counter_onClick_0". - 2
Browser: parse HTML
The browser receives fully-rendered HTML. No framework JS executes. The page is visually complete and readable the instant the HTML parses.
- 3
qwikloader registers one global listener
qwikloader.js(~1 KB, inlined) attaches a single event listener to the document. It intercepts every bubbling event and looks up the matching QRL attribute on the target element. - 4
First user interaction: fetch the handler chunk
qwikloader reads the QRL attribute, fetches that one JS chunk from the CDN, and executes the handler. State is already deserialized — no component re-execution needed.
- 5
Signal update patches the DOM
The handler mutates a
useSignalvalue. Qwik's fine-grained reactivity patches only the affected DOM nodes — no full re-render of the component tree.
Counter with component$ and useSignal
Every $-suffixed boundary is split into its own lazy chunk by the optimizer. useSignal(0) creates reactive state that is serialized into the HTML on the server and deserialized transparently on the client.
import { component$, useSignal } from "@builder.io/qwik";
export const Counter = component$(() => {
const count = useSignal(0);
return (
<button onClick$={() => count.value++}>
Count: {count.value}
</button>
);
});useSignal(0) state is serialized into the HTML. The onClick$ handler becomes its own chunk — downloaded only when the button is first clicked.
Tradeoffs
Pros
- O(1) Time to Interactive regardless of app size — no JS runs on load.
- Fine-grained lazy loading: only the triggered handler's chunk is downloaded.
- Eliminates hydration waterfalls common in large SSR apps.
- Server state is already the source of truth; no double-data problem.
Cons
- First interaction adds a network round-trip to fetch the handler chunk.
- The optimizer and dollar-boundary rules add build complexity.
- Ecosystem is smaller than React/Vue — fewer libraries, less community tooling.
- Mental model shift: developers must internalize the $ boundary and serialization rules.
$ boundary rules are strict
Passing non-serializable values (closures, class instances) across a $ boundary causes a runtime error. Importing a large library inside a $ callback pulls the whole library into that chunk, defeating lazy loading.
Interview angle
Interviewers probe the replay-vs-resume distinction and what exactly gets serialized. Strong answers name the three serialized pieces (state, tree, listeners as DOM attributes) and explain why startup is O(1). Mention the first-interaction chunk fetch as the real tradeoff.
Soundbite: "Resumability serializes state and listeners into HTML so the browser resumes, not replays — O(1) startup, pay-per-interaction JS cost."
Key terms
- Resumability
- Skipping hydration by serializing app state and listener references into HTML for instant startup.
- QRL (Qwik URL)
- A lazy, serializable reference to a code chunk encoded as a URL + exported symbol (e.g. `./chunk.js#Symbol`), stored as a DOM attribute so Qwik lazy-loads code without running JS upfront.
- Dollar boundary ($)
- The Qwik optimizer marker that splits a function into its own lazy-loaded chunk.
- useSignal
- A Qwik hook that returns a reactive `Signal<T>` whose `.value` is serialized into HTML.
- qwikloader
- A ~1 KB global script that intercepts DOM events and fetches the matching handler chunk on demand.