Frontend Development 15 min read

Unlock Webpack splitChunks & Manifest: A Hands‑On Guide to Smarter Bundling

This tutorial walks through the fundamentals of Webpack’s splitChunks and manifest features, explaining modules, chunks, and bundles, detailing configuration options like chunks, cacheGroups, minChunks, priority, maxInitialRequests, and maxAsyncRequests, and shows practical code examples and visual results to help developers master efficient asset bundling.

Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Unlock Webpack splitChunks & Manifest: A Hands‑On Guide to Smarter Bundling

1 Background

For anyone new to building projects, Webpack often feels like an insurmountable hurdle because of its abstract concepts and concise documentation.

The author rarely writes Webpack configurations from scratch, so some settings are not completely clear.

A recent requirement to add MD5 version numbers to page resources gave an opportunity to revisit and reorganize the project's Webpack configuration.

This article skips basic concepts such as entry and output and focuses on the splitChunks and manifest sections.

2 Basic Concepts

To understand

splitChunks

, you need to grasp three core ideas: module, chunk, and bundle.

module : each imported file is a module (the code you write yourself).

chunk : when source files are fed to Webpack, it generates chunks based on import relationships.

bundle : the final compressed output that can be executed directly.

An illustration of these concepts:

module chunk bundle diagram
module chunk bundle diagram

Chunks come in three types:

Project entry (entry).

Dynamically imported code.

Code split out by

splitChunks

.

3 splitChunks Overview

splitChunks

is used to split bundles, extracting code that meets certain rules into separate packages to isolate common modules and reduce duplication.

The configuration items inside

splitChunks

define the splitting rules; the

cacheGroups

option must satisfy all its conditions to take effect.

In Webpack 5, the default configuration for

splitChunks

is:

<code>module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async',
      // Only split when the content exceeds minSize
      minSize: 20000,
      // Ensure the remaining chunk after splitting is larger than this value to avoid zero‑size modules
      minRemainingSize: 0,
      minChunks: 1,
      maxAsyncRequests: 30,
      maxInitialRequests: 30,
      // Force split when size exceeds this threshold, ignoring minRemainingSize, maxAsyncRequests, maxInitialRequests
      enforceSizeThreshold: 50000,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          // Reuse an existing chunk if it has already been split from the main bundle
          reuseExistingChunk: true,
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
};
</code>

The following sections explain the configuration items that were not commented.

3.1 chunks

The

chunks

option can be

all

,

async

, or

initial

; the default is

async

.

Consider this example with

entry1.js

that dynamically imports

page1.js

(React syntax requires Babel).

entry1.js

<code>import React from 'react';
import ReactDom from 'react-dom';

const App = () => {
    let Page1 = null;
    import('./page1.js').then(comp => {
        Page1 = comp;
    });
    return (
        <div>
            <div>App</div>
            <Page1 />
        </div>
    )
}

ReactDOM.render(<App />, document.getElementById('root'))
</code>

page1.js

<code>import _ from 'lodash';
import React from 'react';

const Page1 = () => {
    return (
        <div>
            <div>Page1</div>
        </div>
    )
}

export default Page1;
</code>

Bundling result (illustrated below):

bundle result
bundle result

Analysis:

main.js

is the entry file and is split into its own chunk.

page1_js.js

is the dynamically loaded file, also a separate chunk.

The third chunk contains the third‑party library

lodash

, extracted according to the

cacheGroups

configuration.

Why is

lodash

extracted while

react

, which is imported by both

entry1.js

and

page1.js

, is not? The answer lies in the

chunks: "async"

setting, which only applies to asynchronously loaded packages.

Because

lodash

is imported in

page1.js

, which is loaded asynchronously, it meets the split rule.

react

is not loaded asynchronously, so it does not trigger the split.

Understanding

chunks: "async"

makes it easy to grasp the behavior of

chunks: "all"

and

chunks: "initial"

:

initial

splits only from entry modules.

all

splits from both entry and async modules.

3.2 cacheGroups

cacheGroups

is the core of

splitChunks

. Each group defines a set of rules for extracting modules. The default

vendors

group matches

node_modules

, so third‑party libraries are placed into a vendor chunk. The

default

group extracts shared user‑defined modules.

Key options in

cacheGroups

:

3.2.1 minChunks

A module is extracted when it is imported at least

minChunks

times, but only counting imports from entry modules (dynamic imports are ignored unless

chunks

is set to

all

).

Example: both

entry1.js

and

entry2.js

import a local

jquery.js

. Since the import count is 2,

jquery

satisfies

minChunks

and is extracted.

When only

entry1.js

remains, the dynamic import of

jquery

in

page1.js

does not count toward

minChunks

, so

jquery

stays in the entry chunk.

3.2.2 priority

The

priority

field determines which rule wins when a chunk satisfies multiple groups. Higher priority wins; if equal, the first defined group wins. This explains why

react-dom

ends up in the

vendors

chunk instead of the

default

chunk.

3.3 maxInitialRequests

This option limits the maximum number of parallel requests for entry files. It prevents excessive chunk splitting that would cause too many HTTP requests.

When multiple chunks qualify for extraction but the limit is reached, Webpack selects the larger chunk to split.

3.4 maxAsyncRequests

Similar to

maxInitialRequests

, but applies to asynchronous modules. It limits the number of parallel async requests.

4 manifest

When a new version of a page is deployed, browsers may still serve cached resources unless the filenames contain a hash. Adding a

contenthash

to the filename solves this, but when one project imports assets built by another project, the hash changes each build, making it hard to know which file to request.

The

manifest

plugin records the mapping between original filenames and their hashed counterparts, enabling accurate requests for versioned assets.

Example manifest generated by

webpack-manifest-plugin

:

manifest example
manifest example

5 Summary

By experimenting with concrete demos, even a daunting tool like Webpack becomes much more approachable. When you encounter a technology that feels intimidating, it signals a knowledge gap—spend time mastering it, and the next encounter will feel like an opportunity to showcase your expertise.

frontendJavaScriptWebpackBuild Toolsmanifestsplitchunks
Tencent IMWeb Frontend Team
Written by

Tencent IMWeb Frontend Team

IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.