Designing a Qwik Application with Resumability: Requirements, Minimal Design, and Operational Checks
A concise guide to designing a Qwik app that leverages resumability: requirements, minimal component structure, trust boundaries, verification steps, failure modes, and when to revisit the design.
10 Aug 2026, 18:42 UTC

Requirements
When adopting Qwik’s resumability model, the primary goals are to eliminate costly hydration, keep the initial HTML payload reasonable, and defer JavaScript execution until a user interaction occurs. The application must be able to serialize its state and event listeners into the markup without breaking functionality, and the development team needs to ensure that all state used in components is serializable (plain objects, arrays, primitives).
Smallest Suitable Design
The minimal Qwik setup that satisfies these requirements consists of three parts:
- Root component that renders static markup and includes the
q:objattribute for serialized state. - Event handler attached via a QRL (Qwik URL) that points to a lazy‑loaded chunk containing only the handler logic.
- State proxy (created with
useStore) that tracks which parts of the UI depend on which state slices.
This design avoids any upfront component hydration; the browser only needs to download the HTML and, on first interaction, the specific JS chunk referenced by the QRL.
Trust/Data Boundaries
The trust boundary lies between the server‑generated HTML and the client‑side JavaScript chunks. The server must be trusted to emit correct q:obj values and valid QRL URLs. Conversely, the client must not modify the serialized state directly; any changes must flow through Qwik’s proxy system, which will serialize the updated state back into the DOM on the next render.
Operational Checks
To verify that resumability is working as intended, perform the following checks:
- Inspect HTML: View the page source and locate attributes like
q:obj="{...}"andonclick="q:slot(...)". Their presence confirms that state and event references are serialized. - Network observation: Open DevTools → Network, reload the page, and note that no JavaScript chunks are downloaded until you click an interactive element. After the click, a chunk named something like
chunk-abc123.jsshould appear. - Time to Interactive (TTI): Run a Lighthouse audit and compare the TTI metric against a comparable hydration‑based framework (e.g., React SSR). A lower TTI indicates that the main thread is not blocked by hydration work.
Example: Adding a Counter Button
Below is a minimal component that demonstrates the above checks. Save it as src/components/counter.tsx.
import { component$, useStore, QRL } from '@builder.io/qwik';
export const Counter = component$(() => {
const store = useStore({ count: 0 });
return (
Count: {store.count}
{
store.count++;
}}>
Increment
);
});
When the server renders this component, the HTML will contain something like:
<div>
<p>Count: <span q:obj="{"count":0}">0</span></p>
<button onclick="q:slot('counter-increment')">Increment</button>
</div>
The onClick attribute is a QRL that points to a lazy‑loaded chunk containing the increment logic. No JavaScript for the counter is executed until the button is clicked.
Failure Modes
If any of the following conditions occur, the resumability guarantee breaks:
- Non‑serializable state: Storing a class instance, function, or DOM node in
useStorecauses Qwik to throw during serialization, resulting in a missingq:objattribute and a client‑side error when the proxy tries to read the value. - Incorrect QRL URLs: If the server mangles the QRL path (e.g., due to a misconfigured base URL), the client will attempt to fetch a non‑existent chunk, leading to a 404 and the event handler never running.
- Excessive state size: Large objects serialized into
q:objinflate the HTML payload, potentially offsetting the gains from avoided hydration. Monitoring the compressed HTML size helps detect this.
Conditions That Would Change the Design
The minimal design would need revision if:
- The application requires server‑side only logic that cannot be expressed as a serializable event (e.g., accessing a secret key). In that case, you would move that logic to an API endpoint and keep the client side purely presentational.
- Target devices have extremely limited network bandwidth, making even the modest JS chunk downloads on first interaction prohibitive. You might then consider splitting the app into multiple entry points or pre‑fetching likely chunks via
link rel="prefetch". - Future Qwik versions introduce a built‑in solution for streaming state updates that reduces the need for
q:objrepetition, allowing a thinner HTML payload.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.