Stopping the Hydration Tax: How Qwik's Resumability Changes TTI
Stop paying the 'hydration tax.' Learn how Qwik uses resumability and a specialized optimizer to eliminate the need to rebuild component trees in the browser, drastically improving TTI.
08 Aug 2025, 15:42 UTC

The Hydration Bottleneck
Most modern JavaScript frameworks follow a pattern: the server renders HTML to get content on the screen quickly, but the page remains a "static painting" until the browser downloads and executes a large JavaScript bundle. This process, called hydration, requires the browser to rebuild the entire component tree in memory and attach event listeners to the DOM. As your application grows, the "hydration tax" increases, delaying Time to Interactive (TTI) and frustrating users on slow devices.
Qwik solves this by replacing hydration with resumability. Instead of restarting the application logic in the browser, Qwik pauses execution on the server and resumes it on the client exactly where it left off.
How Resumability Works
Resumability is achieved by serializing the application state and event listeners directly into the HTML. When the browser receives the page, it doesn't need to execute JS to know how the page should behave; the instructions are already there in the markup.
The Role of the Optimizer
To make this possible, Qwik uses a build-time optimizer. This tool transforms your code, breaking components into tiny, independently loadable chunks. These chunks are referenced via QRLs (Qwik URL references), which are essentially pointers to the specific JavaScript needed for a specific interaction.
Fine-Grained Lazy Loading
Unlike traditional code-splitting, which loads entire routes or large components, Qwik loads only the specific function required for a user action. If a user clicks a button, Qwik fetches the small JS chunk associated with that button's click handler and executes it immediately, without ever having to "boot up" the rest of the page.
Practical Implementation: Serialized State
To ensure the client can resume where the server stopped, state must be serializable. In Qwik, you cannot use standard local variables for state that needs to persist across the server-client boundary; you must use specific primitives like useStore.
// Example: A counter component in Qwik
import { component$, useStore } from '@builder.io/qwik';
export default component$(() => {
// useStore creates a proxy that Qwik can serialize into HTML
const state = useStore({ count: 0 });
return (
<button onClick$={() => state.count++}>
Count is: {state.count}
</button>
);
});
What happens under the hood:
- The
onClick$suffix tells the optimizer to extract that function into a separate file. - The
useStorestate is serialized into aq:objattribute in the HTML. - The browser renders the button instantly. No JS is downloaded until the user actually clicks the button.
Trade-offs and Limitations
Resumability is not a free lunch. There are specific engineering trade-offs to consider:
- HTML Payload Size: Because state and event metadata are embedded in the HTML, your initial document size will be larger than a traditional SSR page.
- Build Complexity: You cannot use Qwik as a simple script tag library. It requires a specialized build step to perform the code transformation.
- Strict State Rules: You must be disciplined with
useStoreanduseSignal. Attempting to use non-serializable data (like complex class instances) in your state will break the resumability chain.
Verifying the Result
To confirm that your application is actually resuming rather than hydrating, follow these diagnostic steps:
- Inspect the Source: Right-click your page and "View Page Source." Look for attributes like
q:obj. If you see serialized JSON state in the HTML, resumability is active. - Monitor the Network Tab: Open Browser DevTools > Network. Refresh the page. You should see almost no JavaScript loaded initially. Click an interactive element; you should see a small
.jschunk load only at the moment of interaction. - Check TTI: Use Lighthouse or Web Vitals. In a large-scale app, you should see a significant gap between First Contentful Paint (FCP) and Time to Interactive (TTI) disappear compared to a hydrated framework.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.