✂️ How we cut our client bundle by 30%
Someone on the team finally looked at our production client bundle, which we hadn't checked in a while, and it was 25.67 MB. We got it down to 18.34 MB without removing a single feature. Most of the wins were stuff we were shipping to customers that had no business being there.
Bundle size is the metric that drifts up one dependency at a time until somebody notices. We noticed, turned on rollup-plugin-visualizer, and read the treemap. That treemap is the most useful 30 seconds of the whole project. You stare at the rectangles and immediately have opinions about every one of them.
A couple of the things we found were genuinely embarrassing.
First, react-scan. It's the dev profiler that draws colored borders around components when they re-render. 484 KB of it was sitting in our root chunk, shipping to every customer on every page load. We had it behind a feature flag, but the flag only controlled whether it ran, not whether it loaded. Two deleted files later, gone.
Second: gpt-tokenizer, 2.3 MB of token tables, in the browser. We only count tokens on the server before sending a chat to the model. It got pulled in because the token-counting helper lived in a shared file next to some type definitions, so the bundler couldn't tell only the server needed it. We moved the helper into a .server.ts file. That chunk went from 2.41 MB to 47 KB. 110 lines moved to a different file, 50x smaller.
The rest was the boring kind of win. Swap Shiki's "every language" bundle for the 2 languages we load on init (5.1 MB to 1.02 MB). Lazy-load a few things we only needed occasionally: PDF export, SQL formatting, emoji, Sentry replay, etc.
The main takeaway is just: go look. We had a 484 KB profiler in production because nobody had opened the analyzer in months.
What's the dumbest thing you've found shipping in your prod bundle? And does anyone actually check this on a schedule, or is it always "someone finally noticed"?


Replies