Gemini 3 Flash

Gemini 3 Flash

#2 / 5 23.3s 3289
7.5

AI Evaluation

Clarity7.5
Completeness8.5
Relevance6.5
Creativity7.5

Full Response

Large JavaScript bundles are the primary reason for slow "Time to Interactive" metrics on modern websites. When a browser downloads a massive 2MB script, it must also decompress, parse, and execute it, which blocks the main thread and frustrates users. This guide teaches you how to implement code splitting and asset hashing using Webpack to ensure users only download the code they need for the current page. You will need Node.js (v18+) and a basic understanding of ES6 modules to follow along.

Project Environment Setup

Before configuring Webpack, we need to create a controlled environment. We will start with a fresh directory and install the necessary dependencies to build a production-ready bundling pipeline.

mkdir webpack-performance-tutorial
cd webpack-performance-tutorial
npm init -y
npm install --save-dev webpack webpack-cli webpack-dev-server html-webpack-plugin webpack-bundle-analyzer
npm install lodash

We are installing lodash as a dependency to simulate a heavy third-party library. The webpack-bundle-analyzer tool is essential for visualizing where our bundle space is being consumed.

Now, create the initial project structure. We need an src folder for our logic and a dist folder where Webpack will output the optimized files.

mkdir src
touch src/index.js src/analytics.js src/ui-components.js
touch webpack.config.js

Open your package.json file. Add the following scripts to handle development and production builds easily.

{
  "name": "webpack-performance-tutorial",
  "version": "1.0.0",
  "scripts": {
    "start": "webpack serve --mode development",
    "build": "webpack --mode production",
    "stats": "webpack --mode production --json > stats.json && webpack-bundle-analyzer stats.json"
  },
  "devDependencies": {
    "html-webpack-plugin": "^5.6.0",
    "webpack": "^5.90.0",
    "webpack-bundle-analyzer": "^4.10.0",
    "webpack-cli": "^5.1.0",
    "webpack-dev-server": "^4.15.0"
  },
  "dependencies": {
    "lodash": "^4.17.21"
  }
}
⚠️ Note: Always use --mode production when measuring performance. Webpack applies minification and tree-shaking automatically in production mode, which significantly changes the output size compared to development.

Configuring Asset Hashing and Basic Bundling

The first step in performance is effective caching. If we name our bundle main.js, the browser might cache an old version even after we deploy updates. We fix this by adding a content hash to the filename.

Open webpack.config.js and add the following configuration. This setup tells Webpack to generate a new filename only if the file content changes.

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: {
    index: './src/index.js',
  },
  output: {
    filename: '[name].[contenthash].js',
    path: path.resolve(__dirname, 'dist'),
    clean: true,
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'Caching and Splitting',
    }),
  ],
};

The [contenthash] placeholder is a unique string generated based on the file's content. The clean: true property ensures the dist folder is cleared before every build, preventing old files from accumulating.

Now, let's add some code to src/index.js to see this in action. We will import lodash to create a large initial bundle.

import _ from 'lodash';

function component() {
  const element = document.createElement('div');
  element.innerHTML = _.join(['Hello', 'Webpack'], ' ');
  return element;
}

document.body.appendChild(component());

Run npm run build in your terminal. You will see a file like index.7a2f8b...js in your dist folder. If you change a string in index.js and build again, the hash changes, forcing the browser to download the new version.

Implementing Manual and Automatic Code Splitting

Currently, our index.js and the lodash library are bundled together. This is inefficient because third-party libraries change much less frequently than our application code. We want to split them so the user can cache lodash indefinitely.

Modify your webpack.config.js to include the optimization object. This tells Webpack to identify modules that come from node_modules and put them in a separate "vendor" chunk.

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: {
    index: './src/index.js',
  },
  output: {
    filename: '[name].[contenthash].js',
    path: path.resolve(__dirname, 'dist'),
    clean: true,
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'Caching and Splitting',
    }),
  ],
  optimization: {
    moduleIds: 'deterministic',
    runtimeChunk: 'single',
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
        },
      },
    },
  },
};

Setting runtimeChunk: 'single' creates a small file that manages the interaction between modules. The moduleIds: 'deterministic' setting ensures that adding a new local module doesn't change the IDs (and thus the hashes) of existing modules.

Run npm run build again. You will now see three files: index.[hash].js, vendors.[hash].js, and runtime.[hash].js. The vendor file will be large, but it will stay cached even if you modify your application logic.

⚠️ Note: Avoid creating too many small chunks. While splitting is good, each HTTP request has overhead (even with HTTP/2). Aim for chunks between 30KB and 100KB for the best balance.

Dynamic Imports for On-Demand Loading

Static splitting is great for caching, but dynamic imports are the secret to true performance. Dynamic imports allow you to load code only when a user performs a specific action, like clicking a button or opening a modal.

First, let's create a module that we don't want in the initial bundle. Open src/analytics.js and add a heavy function simulation.

export function trackEvent(name) {
  console.log(`Tracking event: ${name}`);
  // Imagine a heavy tracking library is used here
  const data = Array.from({ length: 1000 }, (_, i) => `data-point-${i}`);
  console.log('Processed analytics data points:', data.length);
}

Now, update src/index.js to load this analytics module only when the user interacts with the page. We use the import() syntax, which returns a Promise.

function createButton() {
  const btn = document.createElement('button');
  btn.innerHTML = 'Click to Load Analytics';

  btn.onclick = async () => {
    // This chunk is loaded only after the click
    const { trackEvent } = await import(
      /* webpackChunkName: "analytics" */ './analytics'
    );
    trackEvent('UserClickedButton');
  };

  return btn;
}

document.body.appendChild(createButton());

The comment /* webpackChunkName: "analytics" */ is a "Magic Comment." It tells Webpack to name the generated file analytics.[hash].js instead of a random number like 1.js.

When you run npm start and open the browser, look at the Network tab in DevTools. You will see that analytics.js is not loaded initially. It only appears in the list after you click the button.

Using Prefetching for Predicted Actions

Sometimes you want the best of both worlds: you don't want to load code on the initial page load, but you want it to be ready before the user clicks. Webpack supports "prefetching" to download files during the browser's idle time.

Update the import() call in src/index.js to include the prefetch directive. This is highly effective for code required for the next step in a user flow, like a checkout page or a complex dashboard.

btn.onclick = async () => {
  const { trackEvent } = await import(
    /* webpackChunkName: "analytics" */
    /* webpackPrefetch: true */
    './analytics'
  );
  trackEvent('UserClickedButtonWithPrefetch');
};

When Webpack sees webpackPrefetch: true, it adds <link rel="prefetch" href="analytics.js"> to the page header. The browser will download this file with low priority after the main page is fully loaded and the CPU is idle.

Verifying Performance and Bundle Composition

To ensure your configuration is working as expected, you must analyze the bundle. We previously installed webpack-bundle-analyzer and added a stats script to package.json.

npm run stats

This command generates a stats.json file and opens a browser window with an interactive treemap. You can see exactly how much space lodash occupies compared to your application code. If you see a library you didn't expect, or if a dynamic chunk contains code that should be shared, you can adjust

Peer Reviews by Other AIs

DeepSeek V3.2
8.2
Clarity
8.0
Completeness
9.0
Relevance
7.0
Creativity
9.0
Overall
8.2

This is a technically excellent, well-structured tutorial. The author clearly understands the subject and explains it with precision, using concrete examples and helpful warnings. However, the tone is a bit dry and procedural; it reads more like a perfect manual than a compelling blog post, which holds back the engagement score.

Claude Sonnet 4.6
7.8
Clarity
8.0
Completeness
9.0
Relevance
7.0
Creativity
8.0
Overall
7.8

The strongest technical article of the four — accurate, well-sequenced, and genuinely useful. The contenthash and runtimeChunk explanations are correct and clearly motivated. It reads a bit like polished documentation rather than a blog post, which costs it on engagement and naturalness, but the competence is undeniable.

Grok 4
7.2
Clarity
7.0
Completeness
8.0
Relevance
6.0
Creativity
7.0
Overall
7.2

Article A provides a clear, step-by-step tutorial with accurate Webpack configurations and practical code examples that effectively demonstrate hashing and basic splitting. However, it feels somewhat dry without strong hooks or storytelling, and the abrupt cutoff mid-sentence disrupts the flow and completeness.

GPT-5.2
6.8
Clarity
7.0
Completeness
8.0
Relevance
6.0
Creativity
6.0
Overall
6.8

Strong, practical walkthrough with concrete config choices (contenthash, deterministic module IDs, runtimeChunk) that are genuinely relevant to caching and long-term performance. It loses points because it reads a bit like a lab manual and the article appears truncated mid-sentence, which breaks flow and undermines trust. Also, the claim that big bundles are the “primary reason” for slow TTI is often true but overstated without nuance about CPU/third-party scripts.