Browsers can load ES modules directly, and an import map lets you use package names without any tooling. The page below does exactly that with the date-fns 36,647 library, then counts the files the browser had to fetch to call one function.
<script type="importmap">
{ "imports": { "date-fns": "https://cdn.jsdelivr.net/npm/date-fns@4.4.0/index.js" } }
</script>
<p id="out" style="font: 20px system-ui, sans-serif">Loading...</p>
<script type="module">
const { formatDistance } = await import('date-fns');
const files = performance.getEntriesByType('resource')
.filter((r) => r.name.includes('date-fns')).length;
document.querySelector('#out').innerHTML =
`${formatDistance(0, 90_000)} of work took <b>${files} requests</b>`;
</script>
One function cost 250 HTTP requests, because index.js re-exports every date-fns module and the browser must fetch them all before any code runs. On a slow mobile connection that waterfall takes seconds, and it is the first problem bundlers solve. A production build does five jobs:
| Job | What happens | Why it helps |
|---|---|---|
| Resolve | Follow imports into a module graph | Finds every needed file |
| Transform | Strip types, compile JSX, lower syntax | Runs in target browsers |
| Tree shake | Drop exports nothing imports | Smaller downloads |
| Bundle and split | Join modules into chunks | Fewer requests, lazy loading |
| Minify and hash | Shorten code, hash file names | Smaller files, long caching |
Tree shaking is easy to see with esbuild 126 ; note that square() never reaches the output:
# src/math.js exports square() and cube(); src/index.js contains:
# import { cube } from './math.js';
# console.log(`3 cubed is ${cube(3)}`);
npx esbuild src/index.js --bundle --format=esm// src/math.js
function cube(x) {
return x * x * x;
}
// src/index.js
console.log(`3 cubed is ${cube(3)}`);Practical advice: prefer ES module builds of libraries (lodash-es, not lodash), avoid default-exported objects full of functions, and load rarely used features with import() so they land in their own chunk. For a page with a few small scripts, native modules without a build step remain a good choice.