When I set this site up, the part I kept going back and forth on was not the layout or the writing setup. It was whether to ship any JavaScript at all.
My instinct said yes. Most of what I work on needs it. Turning it off felt like agreeing to build with one hand tied, so before I did anything I went looking for numbers.
The HTTP Archive crawls millions of real pages every month, so its 2024 report is about as close to ground truth as this gets.
| Median mobile page, 2024 | |
|---|---|
| JavaScript downloaded | 558 KB |
| Never actually runs | 206 KB (44%) |
| Separate script files | 22 |
The payload grew 14% in one year. That is the middle of the pack, so half the web is worse.
The second row is the one I keep coming back to. Close to half the code on a typical page gets downloaded, unpacked, and then never does anything. That is not the cost of features. It is the cost of something else, and I wanted to know what.
The browser is redoing work the server already finished
Think about what a server knows while it builds a page. It knows which pieces of the interface exist. It knows where each one starts and stops, what data it holds, and which buttons do something when you click them.
Then it throws all of that away and sends plain HTML.
The HTML keeps none of it. If the server produced <button>10</button>,
nothing in that markup remembers it came from a counter, or that clicking it
should add one. The knowledge existed for a moment on the server and was
discarded on the way out.
So the browser works it out again from scratch. It downloads the code, runs it a second time, rebuilds the structure the server already had, and only then wires up the clicks. The usual name for this is hydration, which makes it sound like a small finishing touch. In practice the page goes out twice: once as HTML a person can read, and once as JavaScript whose main job is explaining that HTML back to the browser.
Miško Hevery, who created Angular and later Qwik, laid this out in 2022 and I have not managed to argue my way past it since. His definition is the part that stuck with me: overhead is work you could delete and still end up in exactly the same place.
Three people who got here before me
I did not work any of this out myself. When I started reading I found three answers to the same problem, and I like that they disagree with each other.
Astro starts from nothing. It turns your components into HTML and strips the JavaScript out, and if one piece genuinely needs to be interactive you mark that piece and only its code gets sent. The idea is older than the framework. Katie Sylor-Miller at Etsy named it in 2019 and Jason Miller, who wrote Preact, described it properly in 2020.
Qwik goes after the cause instead of the symptom, which is the version I find hardest to stop thinking about. If the browser is rebuilding things only because the server discarded them, then stop discarding them. Qwik writes the handler into the markup itself:
<button on:click="./chunk.js#handler_symbol">click me</button>A single listener at the top of the page catches the click, reads that attribute, and fetches just the code that click needs. Nothing runs before you touch something. Qwik describes this as resuming the page rather than rebuilding it, and that distinction is the whole framework.
Svelte's answer is the one I used, and it is almost boring. SvelteKit has a page option, and the docs do not dress it up: "Disabling CSR does not ship any JavaScript to the client."
export const csr = false;That is the entire change here. One line in one file, and every page on this site became a document.
What it cost me
Less than I expected, but not nothing.
Anything that needs to remember something in your browser is gone. No search box that narrows as you type, no theme switch, no comment form. If I want one badly enough I delete that line, and I would, because those are real features and no amount of arguing about architecture makes them appear for free.
The loss I actually braced for was page transitions. Clicking a link on a site like this used to mean a white flash and a jump. It mostly does not anymore:
@view-transition {
navigation: auto;
}Three lines of CSS, no code running, and the browser fades between two entirely
separate pages. I want to be straight about the state of it, though. MDN still
lists
@view-transition
as limited availability, meaning some major browsers do not support it yet, and
it only works between pages on the same domain. Where it is missing you get a
normal page load, which is what you had before, so nothing breaks.
Where I landed
I am not going to build my day job this way. The things I work on keep real state in the browser, and pretending otherwise would just be a different kind of waste.
But this site is a document. It is words, links, and a list of things I have made, and it was never going to be anything else. Deciding that once, honestly, is what let me delete everything else.
That 558 KB is not the price of interactivity. Most of it is the price of never having asked whether a page needed to be an application in the first place.