CodingNic

Modules & Code Organization

What a Bundler Does

Modules & Code Organization 15 min read

What a Bundler Does

Objectives

By the end of this chapter, you should be able to:

  • Explain why real projects usually don’t ship raw ES modules straight to a browser
  • Describe what a bundler actually does to a project’s files
  • Recognize common bundler features: minification, tree-shaking, and code splitting

💡 Why this matters: Everything in this module works directly in a browser, no extra tools required. Almost every real production project still uses a bundler on top of it. Knowing what one does, even without setting one up yourself yet, makes the tooling in a real job’s codebase make sense instead of feeling like a black box.

The Problem: Native Modules Don’t Scale Perfectly

Everything from this module, import, export, <script type="module">, works in every modern browser with no build step at all. For a small project, that’s genuinely enough. Two problems show up as a project grows:

Every import is a separate network request. A project with 60 small module files means the browser makes 60 separate requests before the page is fully ready, slower than it needs to be, even though each individual file is small.

Browser modules also can’t use a bare package name. Last lesson mentioned this: import _ from "lodash" fails in a browser, import needs an actual path or URL, browsers have no built-in concept of an installed package the way Node does with node_modules.

What a Bundler Does

A bundler (Webpack, Vite, esbuild, and others are common names you’ll see) reads your project’s entire module graph, every file, following every import starting from one entry point, and outputs a much smaller number of files, often just one or a handful, ready to ship to a browser.

Concretely, a bundler typically handles:

  • Combining files. Dozens of small modules become one (or a few) output files, cutting down the number of requests a browser needs to make.
  • Resolving packages. import _ from "lodash" works, because the bundler looks lodash up in node_modules itself and includes the actual code, the browser never sees the bare import, only the final, resolved output.
  • Minification. Variable names get shortened, whitespace and comments get stripped, shrinking the file size sent over the network.
  • Tree-shaking. If a module exports five functions and your project only ever imports two of them, a bundler can leave the other three out of the final output entirely, since static import/export (unlike dynamic, runtime-determined code) makes it possible to know exactly what’s actually used.
  • Code splitting. Remember dynamic import() from last lesson? A bundler recognizes it and automatically splits that code into its own separate output file, loaded only when that import() line actually runs, exactly the lazy-loading behavior from last lesson, just automated at build time.

What This Looks Like Day to Day

Without a bundler, you write and ship the same files:

text
<script type="module" src="main.js"></script>

With a bundler, you write the same import/export code you already know, the bundler runs as a separate step (usually a terminal command like npm run build) and produces an output file, commonly something like dist/bundle.js, that the actual HTML page loads instead of your original source files:

text
<script src="dist/bundle.js"></script>

Notice this final tag doesn’t even need type="module", the bundler’s output is typically one plain script, all the module resolution already happened at build time, not in the browser.

You’re Not Setting One Up Yet

This course keeps working with native import/export, exactly as covered in this module, since that’s what actually teaches how modules work. Setting up and configuring a real bundler is a tooling skill on its own, and belongs in Course 3, Professional JavaScript, alongside the rest of this course’s project tooling. For now, the goal is recognizing what a bundler is doing when you see one in a real project’s config files, not configuring one yourself.

Try It

These are thinking questions, not code, there’s nothing to run here.

  1. A project has 40 small module files and no bundler. Explain, in your own words, one concrete downside of shipping it to a browser exactly as-is.
  2. A module exports 10 functions. A different file imports only 2 of them. Explain what tree-shaking would do in this situation, and why static import/export (rather than, say, a dynamically computed one) is what makes it possible.
  3. Explain the connection between dynamic import() (last lesson) and code splitting (this lesson), in your own words.

Recap

  • Native ES modules work directly in the browser, but many small files mean many requests, and bare package imports like "lodash" don’t resolve without help.
  • A bundler reads a project’s whole module graph and outputs fewer, optimized files: combining modules, resolving packages, minifying, tree-shaking unused exports, and splitting code around dynamic imports.
  • This course continues using native modules directly. Setting up a real bundler is covered later, in Course 3.

Next lesson: this module’s exercises, practicing named exports, default exports, and dynamic imports together.