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

# dev.lazyCompilation

- **Type:**

```ts
type LazyCompilationOptions =
  | boolean
  | {
      /**
       * Enable lazy compilation for entries.
       */
      entries?: boolean;
      /**
       * Enable lazy compilation for dynamic imports.
       */
      imports?: boolean;
      /**
       * Specify which imported modules should be lazily compiled.
       */
      test?: RegExp | ((m: Module) => boolean);
      /**
       * The path to a custom runtime code that overrides the default lazy compilation client.
       */
      client?: string;
      /**
       * Tells the client the server URL that needs to be requested.
       */
      serverUrl?: string;
      /**
       * Customize the prefix used for lazy compilation endpoint.
       * @default "/lazy-compilation-using-"
       */
      prefix?: string;
      /**
       * Enable lazy compilation for Node targets.
       * The application must retain and update the emitted runtime.
       * @default false
       */
      node?: boolean;
    };
```

- **Default:**

```js
const defaultOptions = {
  imports: true,
  entries: false,
};
```

Enable lazy compilation (compilation on demand), implemented based on Rspack's [lazy compilation](https://rspack.rs/guide/advanced/lazy-compilation) feature.

## Introduction

Although Rspack itself has good performance, the overall build time can still be less than ideal when building applications with a large number of modules. This is because the modules in the application need to be compiled by various loaders, such as `postcss-loader`, `sass-loader`, `vue-loader`, etc., which introduce additional compilation overhead.

Lazy compilation is an effective strategy to improve the startup performance of the development phase. Instead of compiling all modules at initialization, it compiles modules on demand as they are needed. This means that developers can quickly see the application running when starting the dev server, and build the required modules in batches. By compiling on demand, unnecessary compilation time can be reduced. As the project scales up, compilation time does not significantly increase, which greatly enhances the development experience.

:::tip
Lazy compilation is only effective for dev builds and does not affect production builds.
:::

## Example

### Enable lazy compilation

For web targets, Rsbuild enables the `imports` option by default, which means lazy compilation of dynamically imported modules.

To enable full lazy compilation functionality, set `lazyCompilation` option to `true`:

```ts title="rsbuild.config.ts"
export default {
  dev: {
    lazyCompilation: true,
  },
};
```

This is equivalent to the following configuration:

```ts title="rsbuild.config.ts"
export default {
  dev: {
    lazyCompilation: {
      imports: true,
      // If there is only one entry, Rsbuild will not enable the entries option by default
      entries: true,
    },
  },
};
```

### Disable lazy compilation

To disable lazy compilation, set `lazyCompilation` to `false`:

```ts title="rsbuild.config.ts"
export default {
  dev: {
    lazyCompilation: false,
  },
};
```

### Entry modules

Use `lazyCompilation.entries` to control whether to lazily compile entry modules:

```ts title="rsbuild.config.ts"
export default {
  dev: {
    lazyCompilation: {
      entries: true,
    },
  },
};
```

With the `entries` option enabled, Rsbuild will not compile all pages when you start the dev server. Instead, it will only compile a specific page when you visit it.

When lazily compiling entry modules, please note:

- It only applies to multi-page applications (MPA) and does not optimize single-page applications (SPA).
- When you visit a page, you need to wait for the page to finish compiling before you can see its content.

### Async modules

Use `lazyCompilation.imports` to control whether to lazily compile [dynamic imported](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import) modules.

```ts title="rsbuild.config.ts"
export default {
  dev: {
    lazyCompilation: {
      imports: true,
    },
  },
};
```

When the `imports` option is enabled, all async modules will only be compiled when requested. If your project is a single-page application (SPA) and you have split the routes using dynamic import, this will significantly speed up the startup time.

### Server URL

Use [lazyCompilation.serverUrl](https://rspack.rs/config/lazy-compilation#serverurl) to tell the client the server URL that needs to be requested.

For web targets, if `lazyCompilation.serverUrl` is not explicitly configured, Rsbuild derives the default `serverUrl` from [dev.client](/config/dev/client.md) when both of the following conditions are met:

- [dev.assetPrefix](/config/dev/asset-prefix.md) is `true` or an absolute URL.
- [dev.client.host](/config/dev/client.md#host) or [dev.client.port](/config/dev/client.md#port) is configured.

When `dev.assetPrefix` is a relative URL, Rsbuild does not infer `serverUrl` from `dev.client`. Lazy compilation requests use the current page origin.

An explicitly configured `lazyCompilation.serverUrl` always takes priority over the default value inferred from `dev.client`.

Example:

```ts title="rsbuild.config.ts"
export default {
  dev: {
    lazyCompilation: {
      serverUrl: 'http://localhost:<port>',
    },
  },
};
```

:::tip
Rsbuild will replace the `<port>` placeholder with the actual port number the server is listening on.
:::

### Node targets

Node targets compile eagerly by default. Set `dev.lazyCompilation.node: true` to defer dynamic imports. Boolean `true` and options without `node: true` remain browser-only. Node entries stay eager, and `entries: true` is rejected. Node lazy compilation requires `mode: 'development'` and `dev.hmr: true`. Other modes are rejected because hot updates must preserve the retained runtime.

For example, configure an ES module bundle for an application-owned native runtime:

```ts title="rsbuild.config.ts"
export default {
  output: {
    target: 'node',
    module: true,
    filename: {
      js: '[name].mjs',
    },
  },
  dev: {
    writeToDisk: true,
    lazyCompilation: { node: true },
  },
  tools: {
    rspack: {
      output: { publicPath: 'auto' },
    },
  },
};
```

The application or framework owns the runtime lifecycle:

- Load the emitted entry once with native `import()` or `require()` and retain that runtime while a lazy import is pending.
- Expose a bridge from the entry that calls `import.meta.webpackHot.check(true)` or `module.hot.check(true)`. Invoke it after a successful compilation, when the update files are available. Apply pending revisions sequentially until the runtime's `__webpack_hash__` matches the successful compilation's hash. Rsbuild installs the HMR plugin but does not drive hot checks.
- Handle update failures and dispose of the runtime, requests, and server on shutdown. An update that the runtime cannot accept requires recovery by the application.

Native runtimes need access to emitted chunks and hot-update files. The example writes them to disk and uses `publicPath: 'auto'` so ESM hot updates resolve relative to the output. For CommonJS output inside a package with `"type": "module"`, use `.cjs` bundle and async chunk filenames, and set `tools.rspack.output.hotUpdateChunkFilename` to `'[id].[fullhash].hot-update.cjs'`.

The Node activation endpoint defaults to an absolute HTTP(S) URL from the dev server's protocol, host, and actual port. It does not use `dev.client` overrides. An explicit HTTP(S) `serverUrl` takes precedence and supports `<port>`. With externally hosted dev middleware, set `serverUrl` to the reachable host endpoint.

The runtime owner must also define which module accepts an update. A self-accepted module does not notify its parents, so previously returned module references can stay unchanged. A lazy boundary can accept a static dependency and expose a getter that reads the updated binding:

```js title="src/boundary.js"
import { value } from './value.js';

export const getValue = () => value;

import.meta.webpackHot.accept('./value.js', () => {});
```

An entry can lazily import this boundary once and call `getValue()` after an update. The changing `value.js` module must let the update propagate to the boundary instead of accepting itself.

Rsbuild does not retain or hot update runtimes created by `environment.loadBundle`. Node output remains excluded from HTTP asset serving. Frameworks must coordinate their acceptance boundaries, route manifests, and loader updates with the retained runtime.

## Version history

| Version | Changes                                                                      |
| ------- | ---------------------------------------------------------------------------- |
| v1.3.0  | Added this option                                                            |
| v1.5.0  | Changed default value from `false` to `{ imports: true, entries: false }`    |
| v2.0.13 | Derived the default `serverUrl` from `dev.client`                            |
| v2.1.7  | Limited `dev.client` inference to `dev.assetPrefix: true` or an absolute URL |
