SplitChunksPlugin
SplitChunksPlugin is a built-in plugin that splits code into multiple chunks to optimize application loading performance and achieve better caching strategies and parallel loading.
SplitChunksPlugin can be configured through the optimization.splitChunks option, and you typically don't need to manually register this plugin.
Basic concepts
- A module is a unit of code or a resource processed by Rspack.
- A chunk groups modules during a build and produces corresponding output assets.
- A cache group is a set of rules for selecting modules and deciding how to split them.
Default behavior
Rspack has a built-in configuration for SplitChunksPlugin that works well for most scenarios.
By default it only affects on-demand chunks, because changing initial chunks would affect the script tags the HTML file should include to run the project.
Rspack ships with two default cache groups:
default: extract modules shared by at least 2 chunks.defaultVendors: extract modules fromnode_modules.
In production mode, the default splitting conditions are:
- The new chunk can be shared by multiple chunks, or it contains modules from
node_modules. - The new chunk is at least 20,000 bytes, based on module sizes before minification and gzip compression.
- On-demand loading requires no more than 30 parallel requests.
- Initial loading requires no more than 30 parallel requests.
When trying to fulfill the last two conditions, bigger chunks are preferred.
In other modes, the default minSize is 10000 bytes, and both maxAsyncRequests and maxInitialRequests default to Infinity (no limit).
Examples
The default value is chunks: 'async', but for most production applications it is recommended to start with:
while keeping the default cache groups. This usually deduplicates both initial and async chunks without changing JavaScript execution semantics. The Rspack runtime still loads only the chunks reachable from the current entry or runtime instance.
Options
Rspack provides a set of options for developers that want more control over this functionality.
The default configuration was chosen to fit web performance best practices, but the optimal strategy for your project might differ. If you're changing the configuration, you should measure the effect of your changes to ensure there's a real benefit.
optimization.splitChunks
This configuration object represents the default behavior of the SplitChunksPlugin in production mode.
When files paths are processed by Rspack, they always contain / on UNIX systems and \ on Windows. That's why using [\\/] in {cacheGroup}.test fields is necessary to represent a path separator. / or \ in {cacheGroup}.test will cause issues when used cross-platform.
splitChunks.automaticNameDelimiter
- Type:
string - Default:
-
When maxSize splits a named chunk, each part gets a suffix appended to the original name. automaticNameDelimiter sets the separator between the name and suffix, which defaults to -.
For example, the following configuration tries to split chunks larger than 100,000 bytes and changes the separator to ~:
In this example, splitting a chunk named main produces names such as main~<hash>. The suffix is a hash because the example uses production mode, where hidePathInfo defaults to true.
splitChunks.cacheGroups
cacheGroups defines cache groups with splitting rules for different modules. Groups can inherit shared options such as chunks, minSize, and minChunks from splitChunks, or override them within the group.
For example, set a lower size threshold for modules under src/components:
componentsnames this set of rules, andtestselects modules undersrc/components.- The group inherits
chunks: 'all', so it applies to both initial and async chunks. - Its
minSize: 20000overrides the global30000; other splitting conditions still apply.
Cache groups also support group-specific options such as test, priority, and reuseExistingChunk, which are explained below.
When you add a custom cache group, Rspack keeps the two default groups, default and defaultVendors. If you do not need one of them, set its entry to false:
enforce
- Type:
boolean
Tells Rspack to ignore the global splitChunks.minSize, splitChunks.minRemainingSize, splitChunks.minChunks, splitChunks.maxAsyncRequests and splitChunks.maxInitialRequests options for this cache group. Values explicitly configured on the cache group still apply.
For example, a global minSize requires at least 20,000 bytes of modules to be extracted. To exempt modules under src/components from this limit, set enforce: true on their cache group:
Component modules selected by test and chunks can then be extracted even if they total less than 20,000 bytes. To bypass limits only when the modules reach a size threshold, use enforceSizeThreshold instead.
idHint
- Type:
string
Adds recognizable text to automatically generated chunk IDs. With optimization.chunkIds: 'named', this text becomes part of the ID.
For example, add a vendors cache group for third-party dependencies and set its idHint to third-party:
The extracted unnamed chunks receive IDs containing third-party, which also appears in filenames that use [id].
Unlike a fixed name, idHint does not merge chunks. With other chunk ID strategies, such as deterministic, the text may not appear in filenames.
layer
- Type:
string | RegExp | ((layer?: string) => boolean)
Selects modules by their layer. For example, first use module.rules[].layer to assign modules under src/modern to the modern layer, then configure a cache group to match that layer:
The example sets minSize: 0 so that even small modules in this layer can be extracted.
priority
- Type:
number - Default:
0
When a module matches multiple cache groups, Rspack prefers the group with the higher priority. A larger number means a higher priority.
Custom groups default to 0. The built-in default and defaultVendors groups use -20 and -10, respectively, so custom groups take priority over both unless you change these values.
For example, give Lodash its own group with a higher priority than the group matching all third-party dependencies:
A module under node_modules/lodash matches both groups. When the splitting conditions are met, Rspack prefers lodash because 20 is greater than 10. Other third-party modules can still be handled by vendors.
reuseExistingChunk
- Type:
boolean - Default:
false(the built-indefaultanddefaultVendorsgroups set it totrue)
Controls whether to reuse an existing chunk. If a chunk already contains all the modules being extracted and no others, Rspack can reuse it.
For example, both Foo and Bar contain shared.js, and Bar contains no other modules:
The following cache group allows Bar to be reused for shared.js. minChunks: 2 requires the module to be shared by at least two chunks, and minSize: 0 allows even a small module to be split:
Bar already contains only shared.js. If it is eligible for reuse, Rspack removes this module from Foo, which then loads Bar to access it.
With reuseExistingChunk: false, Rspack creates a new chunk for shared.js. The emptied Bar may be removed, so the resulting chunk names and filenames can change.
test
- Type:
RegExp | string | (module: Module, { chunkGraph: ChunkGraph, moduleGraph: ModuleGraph }) => boolean
Selects the modules a cache group should extract. It works together with chunks:
chunksdetermines which chunks to extract modules from, such as only on-demand chunks.testdetermines which modules to extract from those chunks, such as only modules fromnode_modules.
For example, this cache group selects third-party dependencies from on-demand chunks only:
Modules that match test must still meet other splitting conditions, such as size and the number of chunks sharing them, before they are extracted. Omitting test makes the cache group consider all modules in the selected chunks, while other conditions still apply.
test supports these forms:
- String: matches the beginning of a module's absolute resource path.
- Regular expression: matches a module's absolute resource path.
- Function: uses the provided module and graph information for custom matching.
To filter by chunk name, use chunks.
Using the function type of test will significantly reduce build performance, as the function needs to be called for each module, resulting in huge cross-language communication overhead between Rust and JavaScript. Therefore, we do not recommend using the function type.
type
- Type:
string | RegExp
Selects modules by their module type. For example, extract plain CSS modules handled by built-in CSS support:
module.rules processes .css files as css modules, and the styles cache group selects those modules. Here, enforce: true allows even small stylesheets to be extracted.
type: 'css' only matches the css module type. It does not match css/module or css/auto.
splitChunks.chunks
- Type:
- Default:
'async'
This option controls which chunks should be selected for code splitting. When a string is provided, the possible values are all, async and initial.
all: Split all types of chunks, including initial chunks and async chunks.initial: Only split initial chunks.async: Only split async chunks.
Generally, setting it to all can help reduce duplicate modules being bundled, as it means chunks can be shared between initial chunks and async chunks.
For most production applications, chunks: 'all' is a useful starting point. A custom cache group with a broad match and a fixed name may bundle modules that some pages do not need.
You can also configure this option for an individual cache group in splitChunks.cacheGroups.{cacheGroup}, for example:
Before Rspack v1.6.2, projects using Module Federation with exposes could not enable chunks: 'all', as it would break remote module splitting.
Starting from v1.6.2, Module Federation works seamlessly with chunks: 'all'.
The chunks option can be set to a regular expression, which is a shorthand for (chunk) => typeof chunk.name === "string" && regex.test(chunk.name).
The chunks option can be set to a function for more fine-grained control. The function receives a chunk parameter, and returning true means the chunk participates in splitting (modules within it may be extracted into new chunks), while returning false means the chunk doesn't participate in splitting (remains as is).
Using the function type of chunks will significantly reduce build performance, as the function needs to be called for each module, resulting in huge cross-language communication overhead between Rust and JavaScript. Therefore, we do not recommend using the function type.
splitChunks.dedupDepth
- Type:
number - Default:
1inproductionmode;0indevelopmentornonemode.
Controls how deeply Rspack searches for modules that chunks can share.
For example, suppose each page initially produces one chunk. { A, B } means that pages A and B both use this code. Extracting the modules they share into a shared bundle can reduce duplication.
dedupDepth: 0: Use page combinations that already exist
Suppose module x is used by pages A and B, while module y is used by pages A, B, and C:
{ A, B } is already the group of pages that use x, and both pages also use y. Rspack can therefore consider putting x and y into a bundle shared by A and B. Depth 0 uses these relationships, where one existing group is contained in another, but does not discover new page combinations.
dedupDepth: 1: Find common combinations for one more round
For a different example, consider three modules used by these pages:
Compare two modules at a time and find the pages that use both:
None of these combinations appeared on its own in the original list. Depth 1 discovers them, so Rspack may extract x and y into a separate chunk that A, B, and C depend on.
dedupDepth: 2: Continue with combinations found in the previous round
In the same example, { A, B, C } and { A, B, D }, found in the previous round, both contain A and B. Comparing them discovers a new combination: { A, B }. Since x, y, and z are all used by those two pages, Rspack can consider putting all three modules into a bundle shared by A and B. The first round could not find this combination.
Higher depths continue this process. It stops early if no new combinations are found. These examples explain how Rspack finds code that could be shared; whether it creates a shared bundle also depends on the other splitChunks options.
Usually, dedupDepth: 1 is sufficient. Increasing the depth can cause computation time to grow exponentially, while further reductions in bundle size may be small.
This option is configured on splitChunks and cannot be overridden by individual cache groups.
splitChunks.defaultSizeTypes
- Type:
string[] - Default:
["javascript", "css", "unknown"]
Specifies which source types to check when options such as minSize use a numeric threshold. The defaults are javascript, css, and unknown.
A source type describes the content a module generates. It differs from the module type matched by a cache group’s type, such as javascript/auto. Sizes are checked separately for each source type present in the modules to extract. JavaScript and CSS sizes are not added together, and a JavaScript-only chunk does not need to meet a CSS threshold.
For example, add wasm to include WebAssembly in size checks, and use '...' to keep the default source types:
splitChunks.enforceSizeThreshold
- Type:
number | Record<string, number> - Default:
50000in production and30000in others
Set a size threshold that lets Rspack continue splitting once a candidate chunk is large enough.
When a candidate reaches this threshold, Rspack ignores splitChunks.minRemainingSize and request-limit checks such as splitChunks.maxAsyncRequests and splitChunks.maxInitialRequests.
Unlike splitChunks.cacheGroups.{cacheGroup}.enforce, this option skips those checks only when the size threshold is met. Smaller candidate chunks remain subject to them.
When configured with a number, the same threshold is applied to all source types defined in splitChunks.defaultSizeTypes. The object form lets you set different thresholds per source type.
You can also configure this option for an individual cache group in splitChunks.cacheGroups.{cacheGroup}, for example:
splitChunks.fallbackCacheGroup
- Type:
After extracting modules according to cacheGroups, Rspack can further split large chunks into smaller parts using maxSize, maxAsyncSize, and maxInitialSize.
At this stage, chunks with size limits from a cache group use those limits. Other chunks use fallbackCacheGroup. For example, these settings can also limit the size of an original chunk that was not extracted by a cache group.
fallbackCacheGroup.chunks determines which chunks can use these settings. Unset options inherit the corresponding global splitChunks settings. This configuration controls the size of existing chunks and does not support selecting modules with test.
For example, the following configuration checks initial and async chunks that have no size limits from a cache group. It attempts to split chunks larger than 50,000 bytes into parts of at least 10,000 bytes:
As with maxSize, this is not a hard upper limit: a chunk may still exceed it if a single module is too large or splitting would violate minSize.
splitChunks.filename
- Type:
string | function
Customizes filenames for initial chunks. All placeholders available in output.filename are supported.
You can also configure this option for an individual cache group in splitChunks.cacheGroups.{cacheGroup}. For example, add a vendors group to customize the filenames of the extracted third-party chunks:
You can also set filename to a function. The following example generates filenames from the chunk ID in pathData, so it also works for unnamed chunks:
splitChunks.hidePathInfo
- Type:
boolean - Default:
truein production mode;falsein other modes
When maxSize splits a named chunk, it adds a suffix to each part's name. hidePathInfo determines whether that suffix contains readable module paths.
For example, set it to true to hide module paths in the suffixes:
Splitting a chunk named main can produce names such as main-<hash> or use numeric suffixes. Changing this option to false allows readable module paths in the suffixes. It only affects names; paths in source maps and other output are unchanged.
splitChunks.maxAsyncRequests
- Type:
number - Default:
30in production mode;Infinityin other modes
Maximum number of parallel requests when on-demand loading.
For example, set the limit to 2:
If an on-demand load needs an async chunk and a shared chunk, it already requires two chunk requests. Rspack may then skip further splitting that would add requests.
This option guides splitting decisions at build time; it does not control how the browser sends requests. maxSize and forced splitting can take precedence and result in more requests.
splitChunks.maxAsyncSize
number | Record<string, number>
Works like maxSize to split large chunks into smaller parts, but applies only to async chunks.
For example, try to split large async chunks into parts of at most 100,000 bytes:
This configuration limits async chunk sizes without limiting initial-only chunks. A large individual module or the minSize requirement may still cause a chunk to exceed the limit. See maxSize for details.
You can configure this option globally in splitChunks, for an individual cache group, or in fallbackCacheGroup.
splitChunks.maxInitialRequests
- Type:
number - Default:
30in production mode;Infinityin other modes
Maximum number of parallel requests at an entry point.
For example, set the limit to 3 and use chunks: 'all' to include initial chunks in splitting:
The entry chunk and its required shared chunks count toward this limit. For example, one entry chunk and two shared chunks already reach three requests, so splitting that adds requests may be skipped.
Like maxAsyncRequests, this option guides splitting decisions at build time. maxSize and forced splitting can take precedence and result in more requests.
splitChunks.maxInitialSize
number | Record<string, number>
Works like maxSize to split large chunks into smaller parts, but applies only to initial chunks.
For example, try to split large initial chunks into parts of at most 150,000 bytes:
This configuration limits initial chunk sizes without limiting async-only chunks. A large individual module or the minSize requirement may still cause a chunk to exceed the limit. See maxSize for details.
You can configure this option globally in splitChunks, for an individual cache group, or in fallbackCacheGroup.
splitChunks.maxSize
- Type:
number | Record<string, number>
Tries to split chunks larger than the specified number of bytes into smaller parts. For example:
This configuration handles both initial and async chunks. It tries to split chunks larger than 100,000 bytes into parts of at least 20,000 bytes. If a single module exceeds the upper limit, or splitting would leave a part smaller than minSize, the result can still exceed maxSize.
Rspack uses stable splitting rules, so changing a module usually affects only some parts. The other parts can remain cached, at the cost of additional requests to load the split chunks.
You can set maxSize globally in splitChunks, for an individual cache group, or in fallbackCacheGroup. For named chunks, automaticNameDelimiter and hidePathInfo control the generated names.
maxSize splitting can exceed maxInitialRequests and maxAsyncRequests, but still needs to respect minSize.
If maxAsyncSize or maxInitialSize is not set separately, it falls back to maxSize.
splitChunks.minChunks
- Type:
number - Default:
1
Sets the minimum number of chunks that must contain a module before it can be extracted. Only chunks selected by chunks count; multiple import statements referencing the module within the same chunk still count as one chunk.
For example, require a module to be shared by at least two chunks:
This configuration keeps the default chunks: 'async', so a module must appear in at least two async chunks and meet the other splitting conditions before it can be extracted.
You can also configure this option for an individual cache group in splitChunks.cacheGroups.{cacheGroup}, for example:
splitChunks.minRemainingSize
- Type:
number | Record<string, number> - Default:
0in development mode; falls back tominSizein other modes
The minimum size, in bytes, of the modules left in the source chunk after splitting. This check applies only when the split has one source chunk, after excluding a reused destination chunk and applying request limits. It prevents splitting from leaving a very small chunk behind.
For example, extracting CSS from a named asynchronous chunk can leave only a small JavaScript module in that chunk. In production mode, minRemainingSize can prevent this split, keeping the CSS in the original named chunk.
The size is checked separately for each source type. A number applies to the types in defaultSizeTypes; an object sets individual thresholds. Source types with no remaining modules or a remaining size of zero are skipped.
You can also configure this option for an individual cache group in splitChunks.cacheGroups.{cacheGroup}, for example:
A cache group's explicit minRemainingSize takes precedence over its own minSize, which in turn takes precedence over the global minRemainingSize. A cache group with enforce: true does not inherit the global value, but its own minRemainingSize and minSize still apply. Reaching enforceSizeThreshold bypasses the remaining-size check.
splitChunks.minSize
- Type:
number | Record<string, number> - Default:
20000in production and10000in others
Sets the minimum size of the modules to extract, in bytes. A numeric value applies the same threshold to each source type in defaultSizeTypes. For example, set the threshold to 100KB:
With this configuration, 80KB of JavaScript and 30KB of CSS each fall below the 100KB threshold. Their sizes cannot be added together to satisfy it.
You can also configure this option for an individual cache group in splitChunks.cacheGroups.{cacheGroup}, for example:
Use an object to set a different threshold for each source type:
This sets the thresholds to 100KB for JavaScript and 300KB for CSS. If the modules to extract generate only JavaScript, only the 100KB threshold applies.
splitChunks.minSizeReduction
- Type:
number | Record<string, number> - Default:
0
Sets the minimum total size reduction across the source chunks, in bytes. minSize checks the size of the modules to extract, while minSizeReduction checks the total size removed from the source chunks.
For example, set the threshold to 100KB:
Suppose two source chunks each contain the same 40KB module. Extracting it into a new shared chunk would remove 40KB from each source chunk, for a total of 40KB × 2 = 80KB. Since this is below the 100KB threshold configured above, the split does not occur.
The threshold counts only the reduction in the source chunks. Including the new 40KB shared chunk, the net reduction across all chunks would be only 40KB, ignoring Rspack runtime and other generated code.
You can also configure this option for an individual cache group in splitChunks.cacheGroups.{cacheGroup}, for example:
splitChunks.name
- Type:
- Default:
false
The name of the split chunk. Providing false will keep the same name of the chunks so it doesn't change names unnecessarily. It is the recommended value for production builds.
You can also configure this option for an individual cache group in splitChunks.cacheGroups.{cacheGroup}, for example:
When name is a function, Rspack calls it with these parameters:
module: the module that is being considered for the split chunk. You can use methods such asmodule.identifier()to derive a stable name from its request or resource path.chunks: the chunks that currently contain this module and were selected by the cache group. Each item is aChunkobject, so common properties such aschunk.nameare available.cacheGroupKey: the key of the cache group that matched the module, for examplevendorsforcacheGroups.vendors.
The function should return the split chunk name. Return undefined to let Rspack fall back to automatic naming for that module group.
For example, you can name vendor chunks by the cache group key and by the selected chunk names:
You can also derive a chunk name from the package that provided the matched module:
name also affects grouping: when different chunk combinations match the same cache group and resolve to the same name, Rspack tries to extract them into the same named chunk. This can improve cache reuse, but may also make pages load modules they do not need.
If your goal is only to hint chunk identity or filenames, prefer splitChunks.cacheGroups.{cacheGroup}.idHint and leave name unset.
If name matches an existing chunk's name, Rspack moves modules into that chunk when the splitting conditions are met. For example, name: 'main' can move matching modules into an existing parent entry chunk named main, while preserving the entry point.
Modules moved from an async chunk into an initial chunk may load earlier. Consider the effect on initial loading when reusing an existing chunk.
splitChunks.usedExports
- Type:
boolean - Default:
truewhen optimization.usedExports istrue; otherwisefalse
Splits shared modules according to which exports each Rspack runtime uses. Entries that use different exports can load separate copies, allowing unused code to be removed from each copy later.
For example, src/shared.js exports two objects:
Three entries import this module: foo and bar use value1, while baz uses value2:
In the following configuration, optimization.usedExports: true analyzes export usage, and splitChunks.usedExports: true uses that information when splitting. chunks: 'all' includes entry chunks, and minSize: 0 allows this small example module to be extracted:
In this example, foo and bar use the same exports, so they can share a chunk containing shared.js; baz uses a separate copy. Setting splitChunks.usedExports to false allows all three entries to share one chunk containing shared.js.
This option controls grouping, not whether unused exports are removed. Removing unused code still depends on tree shaking and minification.
FAQ
Why are modules still duplicated?
Check the following in order:
- Selection: Does
chunksinclude these chunks? Do the cache group'stest,type, orlayermatch the modules? The defaultchunks: 'async'does not extract modules from entry chunks. - Size requirements: Do the modules meet
minSize, and does the reduction in the source chunks meetminSizeReduction?minRemainingSizecan also limit how small the source chunks become. - Sharing: Does the number of chunks participating in the split meet
minChunks? - Request limits: Would the split exceed
maxAsyncRequestsormaxInitialRequests?
Also, with usedExports enabled, entries that use different exports may keep separate copies of a module so that each copy can drop code it does not need.
Very small duplicates are often intentional. Extracting them into an extra chunk can be worse than keeping them duplicated.
Does splitChunks affect JavaScript execution order?
No. splitChunks changes chunk topology, but the Rspack runtime still loads and executes JavaScript according to the dependency graph.
Does splitChunks affect tree shaking?
The two work together. splitChunks extracts and shares modules. With usedExports enabled, it also groups modules by the exports used in each Rspack runtime, allowing separate copies to drop code they do not need. Whether unused code is ultimately removed still depends on tree shaking and minification.
Can splitChunks affect CSS order?
Yes, potentially. This caveat is about extracted CSS, not JavaScript execution order.
In extracted CSS flows such as mini-css-extract-plugin or Rspack's Built-in CSS support, changing chunk groups with splitChunks can also change final CSS order. See Deep dive into webpack CSS order issue for background. If CSS order is the primary concern, also review CssChunkingPlugin.
This page is adapted from webpack documentation under the CC BY 4.0, with modifications.

