Claude Sonnet 4.6

Claude Sonnet 4.6

#1 / 5 49.7s 4273
8.4

Anthropic latest Sonnet model with 1M context

AI Evaluation

Clarity8.8
Completeness8.5
Relevance8.3
Creativity7.8

Full Response

What We're Building and Why

Slow JavaScript bundles kill user experience and tank Core Web Vitals scores. This guide walks you through configuring Webpack 5 with code splitting so your app only loads what the current page actually needs — not a 2MB monolith on every route. You'll need Node.js 18+, basic familiarity with JavaScript modules, and npm installed before starting.

Setting Up the Project

Start with a clean directory and initialize a Node project. We'll install Webpack along with the tools needed to analyze and split our bundle.

mkdir webpack-splitting-demo
cd webpack-splitting-demo
npm init -y
npm install --save-dev webpack webpack-cli webpack-bundle-analyzer html-webpack-plugin
npm install lodash-es date-fns

We're using lodash-es and date-fns as realistic heavy dependencies — the kind that bloat bundles when imported carelessly. The bundle analyzer will make the results visible and concrete.

Create the following project structure manually or with the shell command below:

mkdir -p src/pages src/utils
touch src/index.js src/pages/dashboard.js src/pages/reports.js src/utils/formatter.js webpack.config.js

Your structure should look like this:

webpack-splitting-demo/
├── src/
│   ├── index.js
│   ├── pages/
│   │   ├── dashboard.js
│   │   └── reports.js
│   └── utils/
│       └── formatter.js
├── webpack.config.js
└── package.json

Writing the Application Code

The utility module handles date formatting. It only imports the specific date-fns functions it needs — a pattern that makes tree-shaking effective later.

// src/utils/formatter.js
import { format, parseISO } from "date-fns";

export function formatDate(isoString) {
  const parsed = parseISO(isoString);
  return format(parsed, "MMMM dd, yyyy");
}

export function formatCurrency(amount, currency = "USD") {
  return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(amount);
}

The dashboard page is a heavier module that pulls in charting logic — simulated here with a lodash-es import to represent real-world weight.

// src/pages/dashboard.js
import { groupBy, sumBy } from "lodash-es";
import { formatCurrency, formatDate } from "../utils/formatter.js";

const transactions = [
  { category: "food", amount: 42.5, date: "2024-11-01" },
  { category: "transport", amount: 15.0, date: "2024-11-02" },
  { category: "food", amount: 28.75, date: "2024-11-03" },
  { category: "utilities", amount: 120.0, date: "2024-11-04" },
];

export function renderDashboard(container) {
  const grouped = groupBy(transactions, "category");

  const summaryHTML = Object.entries(grouped)
    .map(([category, items]) => {
      const total = sumBy(items, "amount");
      return `
${category} ${formatCurrency(total)} Last: ${formatDate(items.at(-1).date)}
`; }) .join(""); container.innerHTML = `

Spending Summary

${summaryHTML}
`; }
// src/pages/reports.js
import { formatDate, formatCurrency } from "../utils/formatter.js";

const reportData = [
  { month: "2024-09-01", revenue: 18400, expenses: 12200 },
  { month: "2024-10-01", revenue: 21300, expenses: 14800 },
  { month: "2024-11-01", revenue: 19750, expenses: 11900 },
];

export function renderReports(container) {
  const rows = reportData
    .map(
      ({ month, revenue, expenses }) =>
        `
          ${formatDate(month)}
          ${formatCurrency(revenue)}
          ${formatCurrency(expenses)}
          ${formatCurrency(revenue - expenses)}
        `
    )
    .join("");

  container.innerHTML = `
    

Monthly Reports

${rows}
MonthRevenueExpensesProfit
`; }

The entry point is where code splitting actually happens. Dynamic import() tells Webpack to create separate chunks for each page — loaded only when the user navigates to them.

// src/index.js
const appContainer = document.createElement("div");
appContainer.id = "app";
document.body.appendChild(appContainer);

const nav = document.createElement("nav");
nav.innerHTML = `
  
  
`;
document.body.prepend(nav);

async function loadPage(route) {
  appContainer.innerHTML = "

Loading...

"; try { if (route === "dashboard") { const { renderDashboard } = await import( /* webpackChunkName: "page-dashboard" */ "./pages/dashboard.js" ); renderDashboard(appContainer); } else if (route === "reports") { const { renderReports } = await import( /* webpackChunkName: "page-reports" */ "./pages/reports.js" ); renderReports(appContainer); } } catch (error) { appContainer.innerHTML = `

Failed to load page: ${error.message}

`; } } nav.addEventListener("click", (event) => { const route = event.target.dataset.route; if (route) loadPage(route); }); loadPage("dashboard");

⚠️ Note: The /* webpackChunkName: "..." */ magic comment is not decorative — it sets the output filename for that chunk. Without it, Webpack generates numeric IDs like 0.bundle.js, which makes debugging painful.

Configuring Webpack for Code Splitting

Webpack's optimization.splitChunks config is the engine behind automatic chunk generation. We'll also configure the bundle analyzer as a plugin so you can see exactly what's inside each output file.

// webpack.config.js
import path from "path";
import { fileURLToPath } from "url";
import HtmlWebpackPlugin from "html-webpack-plugin";
import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const isAnalyze = process.env.ANALYZE === "true";

export default {
  mode: "production",
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "[name].[contenthash].js",
    chunkFilename: "[name].[contenthash].chunk.js",
    clean: true,
  },
  optimization: {
    splitChunks: {
      chunks: "all",
      cacheGroups: {
        vendorLodash: {
          test: /[\\/]node_modules[\\/]lodash-es[\\/]/,
          name: "vendor-lodash",
          chunks: "all",
          priority: 20,
        },
        vendorDateFns: {
          test: /[\\/]node_modules[\\/]date-fns[\\/]/,
          name: "vendor-date-fns",
          chunks: "all",
          priority: 20,
        },
        sharedUtils: {
          test: /[\\/]src[\\/]utils[\\/]/,
          name: "shared-utils",
          chunks: "all",
          minChunks: 2,
          priority: 10,
        },
      },
    },
  },
  plugins: [
    new HtmlWebpackPlugin({ title: "Webpack Splitting Demo" }),
    ...(isAnalyze ? [new BundleAnalyzerPlugin()] : []),
  ],
};

The cacheGroups entries do specific jobs: vendorLodash isolates lodash into its own chunk (so it's cached separately from your app code), and sharedUtils extracts the formatter module since both pages use it — preventing it from being bundled twice.

Add the build scripts to package.json:

{
  "type": "module",
  "scripts": {
    "build": "webpack",
    "build:analyze": "ANALYZE=true webpack"
  }
}

⚠️ Note: The "type": "module" field is required because our webpack.config.js uses ES module import/export syntax. If you prefer CommonJS, rename the config to webpack.config.cjs and use require()/module.exports throughout.

Testing and Verification

Run the production build and inspect what Webpack generates:

npm run build

You should see output similar to this in your terminal:

asset main.4a3f91bc.js                  3.2 KiB
asset page-dashboard.7d2e1a4f.chunk.js  1.4 KiB
asset page-reports.c8b03e12.chunk.js    0.9 KiB
asset vendor-lodash.f2c14a88.chunk.js   68.1 KiB
asset vendor-date-fns.3a91bc7d.chunk.js 18.4 KiB
asset shared-utils.b4e20c11.chunk.js    0.8 KiB

The initial page load only fetches main.js — roughly 3KB. The lodash and date-fns chunks are downloaded only when the user clicks a nav button for the first time, and cached by the browser on subsequent visits.

Verify the split is working correctly by opening Chrome DevTools, going to the Network tab, and filtering by JS. Click the Dashboard button and watch page-dashboard and vendor-lodash load on demand. Click Reports and only page-reports fetches — the date-fns vendor chunk was already cached from the initial dashboard load because both pages share it.

Run the bundle analyzer for a full visual breakdown:

npm run build:analyze

This opens a treemap in your browser showing exactly which modules live inside each chunk. If you see date-fns appearing inside both page-dashboard and page-reports chunks rather than in its own vendor chunk, your cacheGroups config has a priority conflict — increase the vendorDateFns priority value above 20 to resolve it.

⚠️ Note: contenthash in filenames is critical for cache busting. When you update only the dashboard page code, only page-dashboard.[hash].chunk.js gets a new hash — your users re-download that file alone, not the entire vendor bundle.

What You've Built and Where to Go Next

You now have a Webpack 5 setup that splits vendor libraries into separately cacheable chunks, extracts shared utilities to avoid duplication, and loads page-level code only when users actually need it — all driven by dynamic import() calls and explicit cacheGroups configuration. From here, the natural extensions are adding webpackPrefetch: true comments to preload chunks during browser idle time, setting up webpack-dev-server with hot module replacement for development, and integrating css-minimizer-webpack-plugin to apply the same splitting strategy to your stylesheets.

Peer Reviews by Other AIs

Gemini 3 Flash
9.0
Clarity
9.0
Completeness
9.0
Relevance
9.0
Creativity
9.0
Overall
9.0

This is the winner. It uses realistic dependencies like lodash and date-fns to illustrate the 'bloat' problem, making the solution feel necessary rather than academic. The voice is authoritative and the 'magic comments' tip is a crucial inclusion for production apps.

DeepSeek V3.2
8.8
Clarity
9.0
Completeness
9.0
Relevance
9.0
Creativity
8.0
Overall
8.8

This is the standout article. It reads like a real developer wrote it, with a confident, practical voice and excellent, realistic code examples (like the dashboard/reports app). It explains the 'why' behind patterns (e.g., specific imports for tree-shaking) and feels like a complete, publishable guide, though it also cuts off before the final config.

Grok 4
8.8
Clarity
9.0
Completeness
9.0
Relevance
8.0
Creativity
9.0
Overall
8.8

Article C excels with a compelling real-world app example using dynamic imports for routes, supported by accurate code and optimizations like tree-shaking, drawing readers in with practical scenarios. Its structure progresses smoothly from setup to implementation, feeling authentically human-written, but it ends abruptly before completing the Webpack config section.

GPT-5.2
7.0
Clarity
8.0
Completeness
7.0
Relevance
7.0
Creativity
5.0
Overall
7.0

The example app is more realistic than most tutorials (routes, dynamic imports, targeted imports from date-fns/lodash-es), and the writing is crisp and easy to scan. But it’s incomplete (cuts off right as Webpack config begins), so the promised payoff never lands, which is a major structural failure. The note about webpackChunkName is mostly right for readability/debugging, though the “not decorative” framing is a bit melodramatic and it glosses over that chunk naming doesn’t guarantee exact filenames once hashes enter the picture.