# Tree shaking and code splitting

> Ship only the code that’s used, and only when it’s needed.

Concept · Building apps · https://levelcode.org/concepts/bundling-code-splitting

Every kilobyte of JavaScript has to be downloaded, parsed, compiled and executed before it does anything. On a mid-range phone, that work often costs more than the download. Bundlers like Vite, Rollup, esbuild and webpack have two main tools to reduce it.

## Tree shaking: drop unused code

ES modules have static `import`/`export` statements, so the bundler can see at build time exactly which exports are used and leave out the rest.

```js
import { debounce } from 'lodash-es'   // only debounce (and what it uses) is bundled
import _ from 'lodash'                 // CommonJS: the whole library, ~70 KB
```

Tree shaking fails when:

- **The package is CommonJS** (`require`/`module.exports`), because its exports are dynamic.
- **A module has side effects at import time** (patching globals, registering things). The bundler has to keep it. Libraries declare `"sideEffects": false` in `package.json` to say they're safe to drop.
- **You import a barrel file** (`index.js` re-exporting everything). Some setups end up pulling in the whole barrel.

## Code splitting: load code when it's needed

A **dynamic `import()`** tells the bundler to put a module and its dependencies in a separate chunk, fetched only when that code runs:

```jsx
const Chart = lazy(() => import('./Chart'))      // separate chunk

<Suspense fallback={<Skeleton />}>
  {showChart && <Chart data={data} />}
</Suspense>
```

Split along these lines:

- **Routes.** Most frameworks do this automatically: each page loads only its own code.
- **Heavy components behind an interaction:** editors, charts, maps, modals.
- **Large libraries used in one place,** like a PDF renderer or a syntax highlighter.

Don't split everything into tiny pieces. Each chunk is a request, and a chain of chunks that each discover the next one creates a waterfall. Preload the chunks you know you'll need next, for example on link hover.

## Measuring

- Use a **bundle visualizer** (`rollup-plugin-visualizer`, `vite-bundle-visualizer`) to see what's large and why.
- Check the **Coverage** panel in Chrome DevTools to see how much of the shipped code actually runs on load.
- Set a **size budget in CI** so regressions fail the build instead of piling up.

Smaller bundles mean faster [hydration](https://levelcode.org/concepts/rendering-strategies) and fewer long tasks on the main thread, which you'll see in [INP and LCP](https://levelcode.org/concepts/core-web-vitals).
