How Qwik’s Resumability Delivers Instant Interactivity with $‑Prefixed Lazy Chunks
Learn how Qwik’s resumability eliminates hydration by serializing state and lazy‑loading code with the $ prefix, plus a worked counter example and trade‑offs.
22 Jan 2026, 16:19 UTC

Problem: Instant Interactivity Without the Hydration Cost
When building a modern web app, developers often choose a single‑page application (SPA) to avoid full‑page reloads. The trade‑off is the hydration step: the server renders HTML, then the client must re‑execute component constructors, rebuild a virtual DOM, and attach event listeners before the page feels responsive. On low‑end devices or slow networks this delay can be noticeable, hurting user experience.
How Qwik’s Resumability Works
Qwik sidesteps hydration by serializing the application state and references to listeners directly into the HTML sent from the server. When the browser loads the page, it can resume execution from that serialized snapshot instead of re‑running component code. The dollar sign ($) prefix marks imports and event handlers as lazy‑loaded; Qwik’s optimizer splits those marked symbols into tiny, independent chunks. A chunk is fetched only when the associated interaction occurs, so the initial JavaScript payload stays minimal.
Key concepts:
- Resumability – the ability to continue execution from a serialized state without re‑hydrating.
- Serialization – converting component state and listener references into a JSON‑like string embedded in the HTML.
- Lazy‑loaded chunk – a separate JavaScript file that contains code marked with
$and is downloaded on demand.
Worked Example: A Click‑to‑Increment Counter
Below is a step‑by‑step guide you can follow in a fresh Qwik project to see resumability in action.
- Create a new Qwik app (if you don’t have one):
npm create qwik@latest my-qwik-app cd my-qwik-app - Add a simple counter component. Replace the contents of
src/routes/index.tsxwith:import { component$, useSignal } from '@builder.io/qwik' export default component$(() => { const count = useSignal(0) return (Count: {count.value}
{ count.value++ }}>Increment ) }) - Build the app for production:
This command runs in the project root; no special permissions are required. The optimizer will generate files undernpm run builddist/. - Inspect the generated HTML. Open
dist/index.htmland look for:- A
<script>block near the top that contains a JSON‑like object labeled__QWIK_STATE__. This is the serialized state. - References to chunks such as
./chunk-abc.jswith the$prefix in the original source.
- A
- Start a preview server and test the behavior:
Opennpm run previewhttp://localhost:4173in a browser. - Open DevTools, go to the Network tab, and disable the cache.
- Click the Increment button once. Observe:
- A request for a lazy‑loaded chunk (e.g.,
chunk-def.js) appears only after the click. - The counter updates instantly without a full page reload.
- A request for a lazy‑loaded chunk (e.g.,
- To verify resumability, reload the page while keeping the same count value displayed. The server‑rendered HTML already shows the correct number, and no additional JavaScript is needed to restore that value.
Trade‑off: Larger Initial HTML
Because the serialized state and listener references are embedded in the HTML, the initial payload can be larger than a traditional SPA that ships only a minimal shell. On very low‑speed connections this extra bytes may increase time‑to‑first‑byte. However, the trade‑off is often worthwhile because the client avoids the costly hydration step and can become interactive sooner after the HTML arrives.
You can check the impact by comparing the size of dist/index.html with the size of the equivalent SPA’s index file (produced by a framework like React or Vue). Use ls -lh dist/index.html in the project root to see the byte count.
Actionable Steps
If you are evaluating Qwik for a project where instant interactivity matters:
- Start with a small feature (like the counter above) to measure the HTML size increase and the lazy‑loading behavior.
- Use the network tab in DevTools to confirm that
$-prefixed chunks are fetched only on user interaction. - Monitor the first‑contentful‑paint (FCP) and time‑to‑interactive (TTI) metrics in Lighthouse to verify that the resumability model improves perceived performance.
By leveraging Qwik’s resumability and the $ syntax, you can write components as if they always run on the server while still delivering fine‑grained, on‑demand code splitting to the client.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.