> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# SplitChunksPlugin

SplitChunksPlugin is a built-in plugin that splits code into multiple [chunks](/misc/glossary.md#chunk) to optimize application loading performance and achieve better caching strategies and parallel loading.

SplitChunksPlugin can be configured through the [optimization.splitChunks](/config/optimization.md#optimizationsplitchunks) option, and you typically don't need to manually register this plugin.

## 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 from `node_modules`.

Rspack will automatically split chunks based on these conditions:

- New chunk can be shared OR modules are from the node\_modules folder
- New chunk would be bigger than 20kb (before min+gz)
- Maximum number of parallel requests when loading chunks on demand would be lower or equal to 30
- Maximum number of parallel requests at initial page load would be lower or equal to 30

When trying to fulfill the last two conditions, bigger chunks are preferred.

## Examples

The default value is `chunks: 'async'`, but for most production applications it is recommended to start with:

```js title="rspack.config.mjs"
export default {
  optimization: {
    splitChunks: {
      chunks: 'all',
    },
  },
};
```

while keeping the default cache groups. This usually deduplicates both initial and async chunks without changing JavaScript execution semantics. Rspack's runtime still loads only the chunks reachable from the current entry or runtime.

## Options

Rspack provides a set of options for developers that want more control over this functionality.

:::warning
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.

```js title="rspack.config.mjs"
export default {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async',
      dedupDepth: 1,
      minChunks: 1,
      minSize: 20000,
      maxAsyncRequests: 30,
      maxInitialRequests: 30,
      cacheGroups: {
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true,
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
};
```

:::warning

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.

:::

:::warning

Passing an entry name to `{cacheGroup}.test` and using a name of an existing chunk for `{cacheGroup}.name` is no longer allowed.

:::

### splitChunks.dedupDepth

- **Type:** `number`
- **Default:** `1` in `production` mode; `0` in `development` or `none` mode.

This option controls how Rspack finds modules shared by different chunks.

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`:

```text
x → { A, B }
y → { A, B, 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:

| Module | Pages that use it  |
| ------ | ------------------ |
| `x`    | `A`, `B`, `C`, `D` |
| `y`    | `A`, `B`, `C`, `E` |
| `z`    | `A`, `B`, `D`, `E` |

Compare two modules at a time and find the pages that use both:

```text
x and y → { A, B, C }
x and z → { A, B, D }
y and z → { A, B, E }
```

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.

```js title="rspack.config.mjs"
export default {
  optimization: {
    splitChunks: {
      chunks: 'all',
      dedupDepth: 1,
    },
  },
};
```

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.cacheGroups

Cache groups can inherit and/or override any options from `splitChunks.{cacheGroup}.*`; but `test`, `priority` and `reuseExistingChunk` can only be configured on cache group level. To disable any of the default cache groups, set them to `false`.

```js title="rspack.config.mjs"
export default {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        default: false,
      },
    },
  },
};
```

### splitChunks.chunks

#### splitChunks.cacheGroups.\{cacheGroup}.chunks

- **Type:**

```ts
type OptimizationSplitChunksChunks =
  'initial' | 'async' | 'all' | RegExp | ((chunk: Chunk) => boolean);
```

- **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](/misc/glossary.md#initial-chunk) and [async chunks](/misc/glossary.md#async-chunk).
- `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 the recommended starting point. What often hurts the result is not `chunks: 'all'` itself, but broad manual cache groups combined with a fixed [`name`](#splitchunksname).

```js title="rspack.config.mjs"
export default {
  optimization: {
    splitChunks: {
      // include all types of chunks
      chunks: 'all',
    },
  },
};
```

:::tip

Before Rspack v1.6.2, projects using [Module Federation](/guide/advanced/module-federation.md) 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)`.

```js title="rspack.config.mjs"
export default {
  optimization: {
    splitChunks: {
      // equivalent to `chunks: (chunk) => typeof chunk.name === "string" && /foo/.test(chunk.name)`
      chunks: /foo/,
    },
  },
};
```

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).

```js title="rspack.config.mjs"
export default {
  optimization: {
    splitChunks: {
      chunks(chunk) {
        // exclude `foo` chunk
        return chunk.name !== 'foo';
      },
    },
  },
};
```

:::warning
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.
:::

You can configure `chunks` individually for each cacheGroup, for example:

```js title="rspack.config.mjs"
export default {
  optimization: {
    splitChunks: {
      cacheGroups: {
        groupA: {
          chunks: 'all',
        },
        groupB: {
          chunks: 'initial',
        },
        groupC: {
          chunks: 'async',
        },
      },
    },
  },
};
```

### splitChunks.maxAsyncRequests

- **Type:** `number`
- **Default:** `30`

Maximum number of parallel requests when on-demand loading.

### splitChunks.maxInitialRequests

- **Type:** `number`
- **Default:** `30`

Maximum number of parallel requests at an entry point.

### splitChunks.enforceSizeThreshold

#### splitChunks.cacheGroups.\{cacheGroup}.enforceSizeThreshold

- **Type:** `number | Record<string, number>`
- **Default:** `50000` in production and `30000` in 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 request-limit checks such as [`splitChunks.maxAsyncRequests`](#splitchunksmaxasyncrequests) and [`splitChunks.maxInitialRequests`](#splitchunksmaxinitialrequests).

Compared with [`splitChunks.cacheGroups.{cacheGroup}.enforce`](#splitchunkscachegroupscachegroupenforce), this is more targeted: only sufficiently large candidates are forced to keep splitting, while smaller ones still follow the normal heuristics.

When configured with a `number`, the same threshold is applied to all size types defined in [`splitChunks.defaultSizeTypes`](#splitchunksdefaultsizetypes). The object form lets you set different thresholds per size type.

```js title="rspack.config.mjs"
export default {
  optimization: {
    splitChunks: {
      enforceSizeThreshold: 80 * 1000,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          enforceSizeThreshold: 120 * 1000,
        },
      },
    },
  },
};
```

### splitChunks.minChunks

#### splitChunks.cacheGroups.\{cacheGroup}.minChunks

- **Type:** `number`
- **Default:** `1`

The minimum times must a module be shared among chunks before splitting.

### splitChunks.hidePathInfo

- **Type:** `boolean`
- **Default:** defaults to `true` if `options.mode` is `'production'`, otherwise defaults to `false`

Prevents exposing path info when creating names for parts splitted by maxSize.

### splitChunks.minSize

#### splitChunks.cacheGroups.\{cacheGroup}.minSize

- **Type:** `number | Record<string, number>`
- **Default:** `20000` in production and `10000` in others

When using the `number` type of configuration, the same `minSize` will be configured for all module types defined in [`splitChunks.defaultSizeTypes`](/plugins/split-chunks-plugin.md#splitchunksdefaultsizetypes).

```js title="rspack.config.mjs"
export default {
  //...
  optimization: {
    splitChunks: {
      minSize: 100 * 1000,
    },
  },
};
```

When configured with the object form, different `minSize` can be set for different types of module types defined in `splitChunks.defaultSizeTypes`.

```js title="rspack.config.mjs"
export default {
  //...
  optimization: {
    splitChunks: {
      minSize: {
        javascript: 100 * 1000,
        css: 300 * 1000,
      },
    },
  },
};
```

For example, the above configuration means that the minimum size of javascript modules in the split chunks needs to be at least 100KB, and the minimum size of css modules needs to be at least 300KB.

### splitChunks.minSizeReduction

#### splitChunks.cacheGroups.\{cacheGroup}.minSizeReduction

- **Type:** `number | Record<string, number>`
- **Default:** `0`

If there are several small modules in the build output, developers may not want to generate separate chunks for them even if their total size exceeds the `minSize` threshold. In this case, you can use the `minSizeReduction` parameter to set the minimum size reduction threshold required for module splitting.

The calculation rule for this parameter is: splitting will only occur when the total size reduction across all parent chunks after splitting the module is not less than the specified value.

Assuming the following scenario, suppose there is a 40KB module that is referenced by 2 chunks, and we set `minSizeReduction: 100`. If we were to split this module, each parent chunk would be reduced by 40KB, resulting in a total reduction of `40KB × 2 = 80KB`. As this is less than 100KB, the split will not be triggered.

If a module still appears duplicated after lowering `minSize`, check `minSizeReduction` before assuming splitChunks is not working.

```js title="rspack.config.mjs"
export default {
  //...
  optimization: {
    splitChunks: {
      minSizeReduction: 100 * 1000,
    },
  },
};
```

### splitChunks.maxSize

`number | Record<string, number> = 0`

Using `maxSize` (either globally `optimization.splitChunks.maxSize` per cache group `optimization.splitChunks.cacheGroups[x].maxSize` or for the fallback cache group `optimization.splitChunks.fallbackCacheGroup.maxSize`) tells Rspack to try to split chunks bigger than `maxSize` bytes into smaller parts. Parts will be at least `minSize` (next to `maxSize`) in size.
The algorithm is deterministic and changes to the modules will only have local effects. So that it is usable when using long term caching and doesn't require records. `maxSize` is only a hint and could be violated when modules are bigger than `maxSize` or splitting would violate `minSize`.

Rspack groups modules for `maxSize` using deterministic path-derived keys, so modules with similar paths tend to stay together. In practice, prefer `maxSize` when the problem is "this shared chunk is too large". It is usually a better fit than forcing a broad fixed [`name`](#splitchunksname).

When the chunk has a name already, each part will get a new name derived from that name. Depending on the value of `optimization.splitChunks.hidePathInfo` it will add a key derived from the first module name or a hash of it.

`maxSize` option is intended to be used with HTTP/2 and long term caching. It increases the request count for better caching. It could also be used to decrease the file size for faster rebuilding.

:::tip

`maxSize` takes higher priority than `maxInitialRequest/maxAsyncRequests`. Actual priority is `maxInitialRequest/maxAsyncRequests < maxSize < minSize`.

:::

:::tip

Setting the value for `maxSize` sets the value for both `maxAsyncSize` and `maxInitialSize`.

:::

### splitChunks.maxAsyncSize

`number | Record<string, number>`

Like `maxSize`, `maxAsyncSize` can be applied globally (`splitChunks.maxAsyncSize`), to cacheGroups (`splitChunks.cacheGroups.{cacheGroup}.maxAsyncSize`), or to the fallback cache group (`splitChunks.fallbackCacheGroup.maxAsyncSize`).

The difference between `maxAsyncSize` and `maxSize` is that `maxAsyncSize` will only affect on-demand loading chunks.

### splitChunks.maxInitialSize

`number | Record<string, number>`

Like `maxSize`, `maxInitialSize` can be applied globally (`splitChunks.maxInitialSize`), to cacheGroups (`splitChunks.cacheGroups.{cacheGroup}.maxInitialSize`), or to the fallback cache group (`splitChunks.fallbackCacheGroup.maxInitialSize`).

The difference between `maxInitialSize` and `maxSize` is that `maxInitialSize` will only affect initial load chunks.

### splitChunks.fallbackCacheGroup

- **Type:**

```ts
type FallbackCacheGroup = {
  chunks?: 'initial' | 'async' | 'all' | RegExp | ((chunk: Chunk) => boolean);
  minSize?: number;
  maxSize?: number;
  maxAsyncSize?: number;
  maxInitialSize?: number;
  automaticNameDelimiter?: string;
};
```

Configures options for modules that are not selected by any cache group. The fallback cache group uses the same sizing behavior as the corresponding global `splitChunks` options, but only for the remaining modules after cache group matching.

```js title="rspack.config.mjs"
export default {
  optimization: {
    splitChunks: {
      fallbackCacheGroup: {
        minSize: 10_000,
        maxSize: 50_000,
      },
    },
  },
};
```

### splitChunks.automaticNameDelimiter

- **Type:** `string`
- **Default:** `-`

By default Rspack will generate names using origin and name of the chunk (e.g. vendors-main.js).

This option lets you specify the delimiter to use for the generated names.

### splitChunks.name

#### splitChunks.cacheGroups.\{cacheGroup}.name

- **Type:**

```ts
type SplitChunksName = false | string | SplitChunksNameFunction;

type SplitChunksNameFunction = (
  module: Module,
  chunks: Chunk[],
  cacheGroupKey: string,
) => string | undefined;
```

- **Default:** `false`

Also available for each cacheGroup: `splitChunks.cacheGroups.{cacheGroup}.name`.

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.

Providing a string allows you to use a custom name. Specifying a string will merge all common modules and vendors into a single chunk. This might lead to bigger initial downloads and slow down page loads.

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 as `module.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 a `Chunk` object, so common properties such as `chunk.name` are available.
- `cacheGroupKey`: the key of the cache group that matched the module, for example `vendors` for `cacheGroups.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:

```js title="rspack.config.mjs"
export default {
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          name(module, chunks, cacheGroupKey) {
            const chunkNames = chunks
              .map((chunk) => chunk.name)
              .filter(Boolean)
              .sort()
              .join('~');

            return `${cacheGroupKey}-${chunkNames || 'shared'}`;
          },
        },
      },
    },
  },
};
```

You can also derive a chunk name from the package that provided the matched module:

```js title="rspack.config.mjs"
export default {
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        npmPackage: {
          test: /[\\/]node_modules[\\/]/,
          name(module, chunks, cacheGroupKey) {
            const identifier = module.identifier();
            const match = identifier.match(
              /[\\/]node_modules[\\/]((?:@[^\\/]+[\\/])?[^\\/]+)/,
            );
            const packageName = match
              ? match[1].replace(/[\\/]/g, '-')
              : 'misc';

            return `${cacheGroupKey}-${packageName}`;
          },
        },
      },
    },
  },
};
```

`name` is not only a filename customization. It also changes grouping behavior: when different chunk combinations hit the same cache group and resolve to the same name, Rspack will merge them into the same named split chunk candidate. This can improve cache hit rate, but it can also force a page to fetch modules from unrelated dependency chains.

If your goal is only to hint chunk identity or filenames, prefer [`splitChunks.cacheGroups.{cacheGroup}.idHint`](#splitchunkscachegroupscachegroupidhint) and leave `name` unset.

If the `splitChunks.name` matches an [entry point](/config/entry.md) name, the entry point will be removed.

:::info

`splitChunks.cacheGroups.{cacheGroup}.name` can be used to move modules into a chunk that is a parent of the source chunk. For example, use `name: "entry-name"` to move modules into the `entry-name` chunk. You can also use on demand named chunks, but you must be careful that the selected modules are only used under this chunk.

:::

### splitChunks.filename

#### splitChunks.cacheGroups.\{cacheGroup}.filename

- **Type:** `string | function`

Allows to override the filename when and only when it's an initial chunk. All placeholders available in output.filename are also available here.

```js title="rspack.config.mjs"
export default {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        defaultVendors: {
          filename: 'vendors-[name].js',
          // or
          filename: (pathData, assetInfo) => {
            return `${pathData.chunk.name}-bundle.js`;
          },
        },
      },
    },
  },
};
```

### splitChunks.usedExports

- **Type:** `boolean`
- **Default:** Value of [optimization.usedExports](/config/optimization.md#optimizationusedexports)

Enabling this configuration, the splitting of chunks will be grouped based on the usage of modules exports in different runtimes, ensuring the optimal loading size in each runtime.

For example, if there are three entry points named `foo`, `bar`, and `baz`, they all depend on the same module called `shared`. However, `foo` and `bar` depend on the export `value1` from `shared`, while `baz` depends on the export `value2` from `shared`.

```js title=foo.js
import { value1 } from 'shared';
value1;
```

```js title=bar.js
import { value1 } from 'shared';
value1;
```

```js title=baz.js
import { value2 } from 'shared';
value2;
```

In the default strategy, the `shared` module appears in 3 chunks. If it meets the [minSize for splitting](/plugins/split-chunks-plugin.md#splitchunksminsize), then the `shared` module should be extracted into a separate chunk.

```
chunk foo, chunk bar
      \
      chunk shared (exports value1 and value2)
      /
chunk baz
```

However, this would result in none of the three entry points having the optimal loaded size. Loading the `shared` module from the `foo` and `bar` entries would unnecessarily load the export `value2`, while loading from the `baz` entry would unnecessarily load the export `value1`.

When the `splitChunks.usedExports` optimization is enabled, it analyzes which exports of the `shared` module are used in different entries. It finds that the exports used in `foo` and `bar` are different from those in `baz`, resulting in the creation of two distinct chunks, one corresponding to the entries `foo` and `bar`, and the other corresponding to the entry `baz`.

```
chunk foo, chunk bar
        \
      chunk shared-1 (exports only value1)

chunk baz
        \
      chunk shared-2 (exports only value2)
```

This option only changes how splitChunks groups modules across runtimes. It does not replace tree shaking and does not decide whether unused exports are removed.

### splitChunks.defaultSizeTypes

- **Type:** `string[]`
- **Default:** `["javascript", "css", "unknown"]`

When calculating the size of chunks, only the sizes of javascript modules and built-in css modules are taken into account by default. For example, when configuring `minSize: 300`, both javascript modules and css modules need to meet the requirement in order to be split.

You can configure additional module types, for example, if you want WebAssembly modules to be split as well:

```js title="rspack.config.mjs"
export default {
  optimization: {
    splitChunks: {
      defaultSizeTypes: ['wasm', '...'],
    },
  },
};
```

### splitChunks.cacheGroups

Cache groups can inherit and/or override any options from `splitChunks.*`; but `test`, `priority` and `reuseExistingChunk` can only be configured on cache group level. To disable any of the default cache groups, set them to `false`.

```js title="rspack.config.mjs"
export default {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        default: false,
      },
    },
  },
};
```

#### splitChunks.cacheGroups.\{cacheGroup}.priority

- **Type:** `number`
- **Default:** `-20`

A module can belong to multiple cache groups. The optimization will prefer the cache group with a higher `priority`. The default groups have a negative priority to allow custom groups to take higher priority (default value is `0` for custom groups).

#### splitChunks.cacheGroups.\{cacheGroup}.test

- **Type:** `RegExp | string | (module: Module, { chunkGraph: ChunkGraph, moduleGraph: ModuleGraph }) => boolean`

Controls which modules are selected by this cache group. Omitting it selects all modules. It can match the absolute module resource path or chunk names. When a chunk name is matched, all modules in the chunk are selected.

:::warning
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.
:::

#### splitChunks.cacheGroups.\{cacheGroup}.enforce

- **Type:** `boolean`

Tells Rspack to ignore `splitChunks.minSize`, splitChunks`.minChunks`, `splitChunks.maxAsyncRequests` and `splitChunks.maxInitialRequests` options and always create chunks for this cache group.

#### splitChunks.cacheGroups.\{cacheGroup}.idHint

- **Type:** `string`

Sets the hint for chunk id. It will be added to chunk's filename.

#### splitChunks.cacheGroups.\{cacheGroup}.reuseExistingChunk

- **Type:** `boolean`
- **Default** `false`

Whether to reuse existing chunks when possible. If so, after splitting, the newly created chunk contains modules that are exactly the same as those in the original chunk, the original chunk will be reused, and no new chunk will be generated, which may affect the final filename of the chunk. For example:

```
chunk Foo: [ module A, module B ]
chunk Bar: [ module B ]

cacheGroup: {
  test: /B/,
  chunks: 'all'
}
```

In chunks Foo and Bar, the module B, due to the configuration of cacheGroup, will be split into a new chunk that only contains module B. This new chunk is identical in terms of the modules it contains with chunk Bar, so chunk Bar can be directly reused.

If the setting of reuseExistingChunk is set to `false`, then the module B in chunks Bar and Foo will be moved to a new chunk, and chunk Bar, since it no longer contains any modules, will be deleted as an empty chunk.

#### splitChunks.cacheGroups.\{cacheGroup}.type

- **Types:** `string | RegExp`

Allows to assign modules to a cache group by module type.

#### splitChunks.cacheGroups.\{cacheGroup}.layer

- **Type:** `string | RegExp | ((layer?: string) => boolean)`

Assigns modules to a cache group by module layer. This is useful when [entry](/config/entry.md#entrydescriptionlayer) or [rule](/config/module-rules.md#ruleslayer) layers are used to separate modern, legacy, server, or client code paths.

```js title="rspack.config.mjs"
export default {
  optimization: {
    splitChunks: {
      cacheGroups: {
        modern: {
          layer: 'modern',
          chunks: 'all',
        },
      },
    },
  },
};
```

## FAQ

### Why are modules still duplicated?

Common reasons include:

- the shared candidate is too small to satisfy [`splitChunks.minSize`](#splitchunksminsize)
- the split does not satisfy [`splitChunks.minSizeReduction`](#splitchunksminsizereduction)
- the candidate does not satisfy [`splitChunks.minChunks`](#splitchunksminchunks)
- [`splitChunks.maxAsyncRequests`](#splitchunksmaxasyncrequests) or [`splitChunks.maxInitialRequests`](#splitchunksmaxinitialrequests) rejects the split
- `chunks`, `test`, or custom cache groups do not actually select the same chunk combination

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 JavaScript loading and execution order are still guaranteed by the runtime dependency graph.

### Does splitChunks affect tree shaking?

No. Tree shaking decides what code is kept. `splitChunks` only decides how kept modules are partitioned into chunks.

### 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](/guide/languages/css.md#built-in-css-support), changing chunk groups with splitChunks can also change final CSS order. See [Deep dive into webpack CSS order issue](https://github.com/orgs/web-infra-dev/discussions/12) for background. If CSS order is the primary concern, also review [CssChunkingPlugin](/plugins/css-chunking-plugin.md).


This page is adapted from [webpack documentation](https://webpack.js.org/configuration/optimization/) under the [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), with modifications.

