A 2MB gzipped JavaScript bundle is ~7MB for V8 to parse and compile. On a mid-range Android at 3G, that is 12 seconds before a single interaction is possible. Bundle size is not an abstract metric — it is directly proportional to Time to Interactive on real hardware.
Why bundle bloat happens silently: Every convenient import adds to the module graph. moment.js locale files, full lodash imports, icon libraries with 1000 icons — none of these produce visible errors. They just make the app slower on devices you don't test on.
What this covers: How bundlers construct the module graph, where tree shaking fails silently, how sideEffects in package.json controls elimination, and how dynamic import() splits the bundle into chunks that load on demand.
What a bundle actually is
When you run npm run build , your bundler (Rollup inside Vite, Webpack, esbuild) does roughly this:
Starting from your entry point ( main.tsx ), it follows every import statement
It builds a module graph a directed graph of every file…