Why we took React and Babel out of our home page
Why a one-page studio site was shipping 4.2 MB of JavaScript, and what it ships now.
On 2026-09-05 we replaced the React + Babel-standalone setup behind oddduck.ooo with plain HTML. The old page downloaded 4,226 KB of JavaScript — Babel 3,064 KB, React DOM 1,055 KB, React 107 KB — and compiled its JSX in the browser before painting anything; on a wired line that alone took 512 ms. The new page ships about 11 KB of vanilla JS and paints in under 100 ms on a local server. Nothing visible changed.
The oddduck.ooo home page has been served as static HTML, without React, since 5 September 2026. Before that, it downloaded 4,226 KB of JavaScript and compiled JSX in the browser before the first pixel appeared. It is 11 KB now.
It started with one remark: "the site seems to take too long to load." We suspected the server first, but the server was fine. The problem was the way we had built the home page.
What was actually slow
fact Before the change, the home page's HTML body was effectively empty. The screen was drawn by React only after three scripts had arrived from an external CDN. The code that builds the screen was not compiled in advance, so every visitor's browser compiled it each time (as of 5 September 2026).
fact On 5 September 2026 we turned off the browser cache on a wired line and downloaded the three files again. This is the result.
| File | Size | Download |
|---|---|---|
| @babel/standalone 7.29.0 | 3,064 KB | 290 ms |
| react-dom 18.3.1 (development) | 1,055 KB | 146 ms |
| react 18.3.1 (development) | 107 KB | 76 ms |
| Total | 4,226 KB | 512 ms |
Three things were stacked on top of each other. First, we were using development builds. With production builds the two React files drop from 1,162 KB to a little over 140 KB. Second, the 3 MB of Babel was purely the cost of compiling JSX in the browser. Third, the body was empty, so the first pixel was only drawn once all of that had arrived and finished compiling. Until then the visitor looked at a blank cream screen.
On a wired line the download alone is 0.5 seconds. On a line with a fifth of that bandwidth, simple arithmetic gives 2.5 seconds. On top of that, parsing a 3 MB script and compiling the JSX fall to the CPU of the visitor's device. The shape of the thing matched the complaint exactly.
For the record, the server was not the problem. fact Measured ourselves on the same day, our server answered the first byte in 100–270 ms, and all of the images together came to 270 KB.
Why we built it that way
This site started as a design prototype. To look at typefaces and copy while changing them, we put a tweaks panel in the corner of the screen. To get that done quickly we used React and compiled the JSX in the browser as well. A no-build rule meeting the urge to see something right away produces exactly this kind of structure. The prototype just went live as it was.
fact That tweaks panel was never once rendered for a visitor. Even so, its 25.8 KB of code was being compiled along with everything else on every visit.
What we replaced it with
The home page is a one-page introduction. The only thing that counts as state is whether the mobile menu is open. So instead of changing framework, we removed the framework.
- We wrote the screen React used to build straight into the HTML. The browser draws it the moment it arrives.
- The menu, the mobile drawer and the quack the duck makes when you press it moved into one small script.
- The status strip and the keyboard shortcuts stayed in the small script that already held them.
- Fonts are now fetched from the head of the document, instead of being pulled one after another from inside the stylesheet. Before, the fonts were fetched in order only after the stylesheet had fully arrived.
- The tweaks panel and the files that needed compiling were deleted.
There is still no build step. We fix a file, upload it, and that is what is served. What we wanted to keep was "no build", not "React".
The result
fact After the change, on a local server (fonts cached): First Contentful Paint 96 ms, DOMContentLoaded 53 ms. Live, on a first visit, DOMContentLoaded 361 ms. The home page loads two scripts, about 11 KB together (measured 5 September 2026).
| Before | After | |
|---|---|---|
| JavaScript | 4,226 KB + 49 KB of JSX | 11 KB |
| Compiling in the browser | every visit | none |
| To the first pixel | after everything arrives and compiles | as soon as the HTML arrives |
| Build step | none | none |
Nothing visible changed. The colors, the type, the duck, the quack, the marquee, the mobile drawer are all the same. The only thing that changed is the route they take to reach the screen.
What we learned
Leave prototype tooling in the live site and the visitor pays for it. In particular, "no build" does not mean "no bundle". Compiling in the browser does not remove the build; it makes every visitor run one.
So our rules gained a line. Do not let React back in. If a reason to change the screen comes up we will think about it again, but no reason will come up to attach a 4 MB compiler to a single introduction page.