Qwik: Resumability That Feels Like React — Manu Martínez-Almeida
Open Source<br>Qwik: Resumability That Feels Like React<br>May 9, 2023 · Manu Martínez-Almeida qwikcompilersrustperformanceweb
Performance is a human problem, not a technology one
The thing I came to believe working on Qwik is that the web’s performance problem isn’t a<br>technology problem. It’s a design and developer-experience problem. Developers, like water, follow the<br>path of least resistance, and in most frameworks the easy way to build something is also the way that<br>ships a slow site. We then tell people to go optimize it afterward, as a chore, trading off<br>capabilities or DX or their weekend to claw the performance back.
I find it more honest to stop blaming developers and fix the path instead. A framework should be<br>designed so the path of least resistance lands you on a fast site by default. You shouldn’t have to be<br>a performance expert to ship a fast page; you should have to work to ship a slow one.
And the thing worth optimizing is almost always JavaScript. Most sites already handle their images and<br>CSS reasonably well, so there’s little left on the table there. JavaScript is where the big wins hide.<br>The difference between a sluggish page and a snappy one is often tens of points of Lighthouse score, and<br>for anyone running an e-commerce or consumer site, that gap is revenue. Resumability is the lever Qwik<br>pulls on exactly that.
The idea: resume, don’t rebuild
Every mainstream framework hydrates. The server renders HTML, then the browser downloads the whole<br>component tree as JavaScript, re-executes it, and reattaches event listeners to reconstruct state the<br>server already had. You pay, in bytes and CPU, to rebuild something you were handed seconds ago.
Islands and partial hydration, the approach Astro popularized, make this better by only hydrating<br>the interactive bits. But each island still has to download and execute its JavaScript before it’s<br>ready, and someone has to draw and maintain those island boundaries by hand.
Resumability skips the rebuild entirely. The server serializes the application’s state and its event<br>wiring into the HTML itself. The browser ships almost no JavaScript up front. Conceptually the app<br>becomes a hashmap: an event on an element points at the one chunk of code that handles it. When you<br>click a button, Qwik reads an attribute on that element, fetches that small chunk, restores the state<br>it captured, and runs it. There’s no global “boot the app” step, because there’s no app to boot.
There’s a neat way to see the flip. In a hydrated app, event handlers are the last thing to become<br>ready, after the whole tree has downloaded and executed. In a resumable app they’re the first. The<br>page is interactive the moment it arrives.
Booting a page: hydrate vs. resume
JS up front 0 KB on click — interactive —
Figure. The same component tree, booted two ways. Hydration walks it bottom-up and the page goes live only when the wave reaches the leaves — last. Resumability ships static HTML that's interactive on arrival, then fetches one handler's chunk per click. KB and time-to-interactive are computed from the tree.<br>The server sends HTML<br>Both approaches start in the same place: the server renders the page and ships the markup.<br>It paints instantly, but nothing responds yet — there are no event handlers attached to any of<br>these nodes. Zero JavaScript has run.
Hydration boots the whole tree<br>To make it interactive, the browser downloads the component tree as JavaScript and<br>re-executes it from the root up, re-attaching listeners node by node to rebuild state the server<br>already had. Watch the wave climb: the 👆 interactive bits at the leaves are the very last<br>thing to wake up.
Resumability: ready on arrival<br>Now the same tree, resumed. The server serialized the state and event wiring into the HTML,<br>so the browser boots almost nothing. Every handler is live the moment the page lands — handlers<br>are the first thing ready, not the last. No re-execution, no rebuild.
One click, one chunk<br>Interactivity costs nothing until you use it. Click a button and Qwik reads an attribute,<br>fetches that one handler's chunk, restores its captured state, and runs it. The cost is<br>flat whether the app has three components or five hundred — never the whole tree.
The first version worked; writing it was the hard part
Proving resumability was one thing. The developer experience was another. In that early version<br>the philosophy leaked all the way into the API. You split your code into many small files by hand, you<br>referenced lazy-loadable symbols by name, and you wrote a lot of ceremony to tell the runtime how the<br>pieces fit together. It worked, and it asked too much of the person typing.
My bet was that all of that ceremony could become the compiler’s job. A developer should write<br>something that looks like ordinary React, with components, event handlers, and local state, and the<br>build step should do the hard work of making it resumable. The...