← Back to all tutorials

Installing Webpack & Bundling JS Files

Install Webpack and its CLI, then create your first bundle — combining multiple JavaScript modules into a single output file.

Installing Webpack & Bundling JS Files

Now that the project is set up, it is time to install Webpack and create your first bundle. In this episode you will install Webpack, run it from the command line, and see how it resolves import/export statements to produce a single output file.

Step 1: Install Webpack

npm install webpack webpack-cli --save-dev
PackagePurpose
webpackThe core bundler engine
webpack-cliCommand-line interface — lets you run webpack in the terminal

Both are installed as devDependencies because they are build tools only needed during development.

Step 2: Add an npm Script

Open package.json and add a build script:

{
    "scripts": {
        "build": "webpack"
    }
}

Running npm run build will execute the locally installed Webpack. You can also run it directly with npx webpack, but npm scripts are the standard approach.

Step 3: Run Webpack

npm run build

Without any configuration file, Webpack uses sensible defaults:

DefaultValue
Entry point./src/index.js
Output file./dist/main.js
Modeproduction (minified output)

Webpack reads src/index.js, discovers that it imports from utils.js and dom.js, and bundles everything into dist/main.js.

Understanding the Output

npm run build

asset main.js 1.2 KiB [emitted] [minimized] (name: main)
./src/index.js 189 bytes [built] [code generated]
./src/utils.js 167 bytes [built] [code generated]
./src/dom.js 198 bytes [built] [code generated]
webpack compiled successfully

The output tells you:

  • One asset was created: main.js at 1.2 KiB
  • Three modules were processed: index.js, utils.js, and dom.js
  • The output is minimized (production mode default)

Updating index.html

Since the default output is main.js (not bundle.js), update the script tag in dist/index.html:

<script src="main.js"></script>

Open dist/index.html in a browser and you should see "Hello, Webpack!" rendered on the page.

Development vs Production Mode

# Production mode (minified, optimized) — default
npx webpack --mode production

# Development mode (readable, with comments)
npx webpack --mode development
ModeOutputSource MapsOptimizations
productionMinified, mangledNone by defaultTree shaking, dead code removal
developmentReadable, with commentseval source mapsFaster rebuilds, no minification

How Webpack Resolves Modules

// In src/index.js:
import { greet, VERSION } from './utils';

// Webpack resolves this to:
// 1. Look for ./utils.js in the same directory
// 2. If not found, look for ./utils/index.js
// 3. If not found, look in node_modules/utils

Webpack follows a resolution algorithm similar to Node.js. It checks file extensions (.js, .json), index files, and node_modules automatically. You do not need to write ./utils.js — just ./utils is enough.

Tree Shaking

In production mode, Webpack performs tree shaking — it removes unused exports from the bundle.

// src/utils.js exports: greet, capitalize, VERSION
// src/index.js imports: greet, VERSION
// capitalize is NOT imported

// In production mode, Webpack removes capitalize from the bundle
// because it is never used — this reduces bundle size

Tree shaking works with ES module syntax (import/export) but not CommonJS (require/module.exports). This is one reason why ES modules are preferred.

Watching for Changes

npx webpack --watch

The --watch flag tells Webpack to monitor your source files and automatically rebuild when any file changes. This saves you from manually running the build command after every edit.

{
    "scripts": {
        "build": "webpack",
        "watch": "webpack --watch"
    }
}

What Happens Inside the Bundle

If you build in development mode and open the output file, you will see that Webpack wraps each module in a function and creates a module registry. Each import is replaced with a call to Webpack's internal __webpack_require__ function:

// Simplified view of what Webpack generates:
var modules = {
    "./src/utils.js": function(module, exports) {
        // utils.js code here
    },
    "./src/dom.js": function(module, exports) {
        // dom.js code here
    },
    "./src/index.js": function(module, exports, require) {
        var utils = require("./src/utils.js");
        var dom = require("./src/dom.js");
        // index.js code here
    }
};

// Start execution from the entry point
modules["./src/index.js"]();

In production mode, all of this is minified and optimized — but the concept is the same.

Key Takeaways

  • Install webpack and webpack-cli as dev dependencies
  • Without a config file, Webpack defaults to ./src/index.js as entry and ./dist/main.js as output
  • Production mode minifies and tree-shakes; development mode produces readable output
  • Webpack follows import statements to discover and bundle all dependencies automatically
  • Tree shaking removes unused exports — only code that is actually imported ends up in the bundle
  • Use --watch to automatically rebuild on file changes