Resumability in practice: shipping state with HTML and deferring handlers in Qwik 1.x
Qwik 1.x avoids hydration by serializing state into server-rendered HTML and resuming on first interaction via QRL handlers. This blog explains the model with a product page example and the serializability constraint.
03 Jul 2026, 21:41 UTC

The product page that waits for a bundle
The concrete problem is a marketing product page with a quantity stepper, an accordion of specs, and tabs for reviews. In a hydrate-on-load SPA the HTML arrives fast, but none of those controls work until the whole JavaScript bundle downloads, parses, and re-runs every component to attach listeners. The page looks interactive but is not.
Qwik 1.x targets this with resumability. The server renders full HTML and serializes the app's initial state into the document. The browser does not hydrate by re-executing components. Instead, it resumes execution on first interaction by loading only the code needed for that interaction. The takeaway is not smaller bundles in the abstract; it is deferring work until a user actually needs it.
How resumability is encoded in the markup
Resumability relies on server rendering, typically via Qwik City. The server output contains two visible artifacts you can check in View Source: q:* attributes on elements and a script block with serialized state. Those attributes and the serialized JSON are what let the runtime reconstruct the component tree without running it up front.
The API reinforces the model. Components are declared with component$(), state is held with useSignal() or useStore(), and every event handler is a QRL. QRL stands for Qwik Resumable Function, a lazily fetched, serializable reference to a function. The reference is written into the HTML; the function body lives in a separate chunk.
When a user clicks a control, the runtime fetches just that handler's chunk, resumes the component graph that owns it, and runs the handler with the state that was already serialized on the server. No upfront hydration pass is required.
A worked example: quantity stepper
The following is a minimal Qwik City component pattern for a stepper. It shows the state shape and the QRL handler without claiming test results.
import { component$, useSignal } from '@builder.io/qwik';
export default component$(() => {
const qty = useSignal(1);
const increment = () => {
qty.value = qty.value + 1;
};
const decrement = () => {
qty.value = Math.max(1, qty.value - 1);
};
return (
<div>
<button onClick$={decrement}>-</button>
<span>{qty.value}</span>
<button onClick$={increment}>+</button>
</div>
);
});
onClick$ is the Qwik convention for a QRL handler. The component and its state are serialized to the HTML. The bodies of increment and decrement are not downloaded until one of the buttons is clicked. You can observe this behavior by opening the network tab in a devtools session for a Qwik City app and clicking the button; a small chunk request appears at click time rather than on initial load.
Route loaders follow the same principle. Data returned from a loader must be serializable because it is embedded in the HTML and shipped to the client. Plain objects, arrays, Map, Set, and Date survive serialization. Class instances with methods, functions, and DOM references do not.
An engineering decision: avoid eager work
Qwik provides useVisibleTask$ to run code when a component becomes visible. The current docs and tooling nudge you away from it because eager on-visible work reintroduces the upfront execution resumability is designed to avoid. The practical decision is to prefer event handlers and route loaders for data fetching, and keep useVisibleTask$ for cases where visibility is truly required, such as starting a video.
An ESLint rule in the ecosystem flags common eager patterns. The rule is version-sensitive, so check the rule guidance for your installed Qwik 1.x version before relying on it as a permanent guarantee.
Trade-offs and limits
Resumability depends on server-rendered HTML. A purely client-rendered Qwik app gives up most of the benefit, which is itself a useful decision to discuss when evaluating deployment constraints.
Serializable state is a hard constraint. Anything held in signals, stores, or returned from loaders must be serializable. Putting a class instance with methods into a store will not resume correctly and will typically surface as an error or warning during development. A practical check is to inspect the serialized payload in the HTML and confirm that complex types have been replaced with plain data.
Ecosystem fit is the second trade-off. React component libraries do not drop in directly. Teams choosing Qwik weigh the interaction model and resumability against the cost of rebuilding or wrapping existing widgets.
What to verify before shipping
- Confirm server rendering is enabled in Qwik City and that the page source contains q:* attributes and a serialized state script block.
- Open the network tab and interact with a control to see a handler chunk load on demand rather than on page load.
- Audit stores and loaders for serializability. Replace class instances with plain objects and move methods to QRL handlers.
- Review uses of useVisibleTask$ and prefer event-driven or loader-driven data.
Resumability is not magic. It is a deliberate shift from hydrate everything up front to serialize state, write QRL references into HTML, and fetch code on first interaction. For pages with many interactive islands, that shift changes when work happens, and it makes serializability a first-class design constraint.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.