跪拜 Guibai
← Back to the summary

Rspack 2.1 Ships Rust-Powered React Compiler: Up to 13x Faster Than Babel

We are excited to announce that Rspack 2.1 has been officially released!


Notable changes include:

Performance Improvements

React Compiler Rust Version

React Compiler is a build-time optimization tool officially released by React. It automatically adds appropriate memoization logic for components and Hooks during the compilation phase, reducing the need for manual use of useMemo, useCallback, and React.memo.

Previously, React Compiler was primarily integrated via babel-loader, which introduced additional Babel transformation overhead and increased project build times. With React Compiler being ported to Rust, SWC has also completed its integration. Rspack 2.1 can now directly enable React Compiler through the built-in SWC loader.

In our benchmarks, the Rust version of React Compiler shows approximately 7-13x performance improvement compared to the Babel version:

Command React Compiler (Rust) React Compiler (Babel) Improvement
rspack dev 0.7 s 10.6 s 13.5x
rspack build 1.2 s 9.3 s 7.4x

Enable React Compiler via jsc.transform.reactCompiler in builtin:swc-loader:

// rspack.config.mjs
export default {
  module: {
    rules: [
      {
        test: /\.(?:js|jsx|ts|tsx)$/,
        use: {
          loader: 'builtin:swc-loader',
          options: {
            detectSyntax: 'auto',
            jsc: {
              transform: {
                react: {
                  runtime: 'automatic',
                },
                reactCompiler: true,
              },
            },
          },
        },
      },
    ],
  },
};

For more configuration options, refer to the Rspack React Compiler Guide.

Build Performance Optimization

Build performance has always been a core focus of Rspack. In benchmarks, Rspack 2.1 shows approximately 16% improvement in production build performance and 5% improvement in HMR performance compared to Rspack 2.0.

Version Production Build (No Cache) Production Build (With Cache) HMR
Rspack 1.7.11 3.12 s 2.09 s 129 ms
Rspack 2.0.0 2.66 s 1.36 s 113 ms
Rspack 2.1.0 2.22 s 1.20 s 107 ms

Data source: rspack-react-10k-benchmark

These improvements come from three main areas: numerous micro-optimizations in the main build pipeline, optimization of underlying data structures like module graphs and dependency graphs, and improvements to the SWC parsing and transformation process.

TypeScript 7 Support

TypeScript type checking is often one of the most time-consuming parts of the build pipeline. ts-checker-rspack-plugin now supports type checking using TypeScript 7 (TypeScript Go). In builds with type checking enabled, the overall time can be reduced by approximately 60%.

Install the TypeScript 7 RC version in your project to use it:

pnpm add typescript@rc -D
// rspack.config.mjs
import { TsCheckerRspackPlugin } from 'ts-checker-rspack-plugin';

export default {
  plugins: [new TsCheckerRspackPlugin()],
};

Faster Circular Dependency Checking

Rspack 2.1 introduces a new CircularCheckRspackPlugin to replace the deprecated CircularDependencyRspackPlugin.

// rspack.config.mjs
import { rspack } from '@rspack/core';

export default {
  plugins: [new rspack.CircularCheckRspackPlugin()],
};

Compared to the old plugin, the new CircularCheckRspackPlugin has two main improvements:

If you are currently using CircularDependencyRspackPlugin, it is recommended to migrate to CircularCheckRspackPlugin. For scenarios where you only need to ignore some warnings, you can use it in conjunction with ignoreWarnings.

New Features

Support for import.meta.glob

Rspack has added support for import.meta.glob. You can collect modules by glob pattern and load them on demand:

const pages = import.meta.glob('./pages/**/*.js');

for (const path in pages) {
  const mod = await pages[path]();
}

This feature has already been implemented in Vite and Turbopack. With Rspack adding support, developers can use a more consistent and familiar syntax across different ecosystem tools, reducing the cognitive cost of switching tools. This also allows framework and library authors who support multiple build tools to reuse more similar implementations.

See the import.meta.glob documentation for complete usage.

Improved Built-in CSS Support

Rspack 2.1 further improves built-in CSS support. The new css/global module type allows CSS Modules to work in a "global by default, :local on demand" manner, covering more scoping organization methods alongside css/module and css/auto.

CSS Modules related capabilities are also being filled in, with support for more CSS Modules syntax and behaviors.

For related configuration, refer to the CSS options in module.generator and module.parser.

Support for Resolving createRequire

ESM modules do not have a built-in require, so Node.js provides module.createRequire(), allowing you to create a require function within ESM to load CommonJS modules. In previous versions, Rspack could not statically analyze require created this way, so modules loaded by it could not be properly bundled.

Rspack 2.1 adds the module.parser.javascript.createRequire option. When enabled, Rspack will recognize createRequire imported from Node.js module and convert the created require into a statically analyzable dependency context. Consequently, modules loaded by it will be bundled into the output like regular require or import statements.

// rspack.config.mjs
export default {
  module: {
    parser: {
      javascript: {
        createRequire: true,
      },
    },
  },
};
// index.js
import { createRequire } from 'module';

const require = createRequire(import.meta.url);
const value = require('./value.cjs');

This option also supports:

This option is disabled by default. See module.parser.javascript.createRequire for more details.

Rspack Magic Comments

Rspack 2.1 adds rspack prefix support for magic comments. You can now use the rspack prefix to declare compilation hints:

import(/* rspackChunkName: "dashboard" */ './dashboard');

The original webpack prefix remains compatible, so existing projects do not need to migrate.

See the magic comments documentation for more details.

Support for Source Phase Imports

Rspack 2.1 supports the WebAssembly usage within the TC39 Source Phase Imports proposal. By enabling experiments.sourceImport, you can import .wasm modules using static import source or dynamic import.source().

Unlike regular WebAssembly imports, source phase imports do not instantiate the Wasm module upon import. Instead, they return the compiled WebAssembly.Module. This allows you to control the instantiation process yourself, for example, instantiating the same Wasm module multiple times with different imports, or reusing the same compiled result across multiple Web Workers to avoid recompilation costs.

// rspack.config.mjs
export default {
  experiments: {
    sourceImport: true,
  },
};
// index.js
import source wasmModule from './module.wasm';

const instance = await WebAssembly.instantiate(wasmModule, {
  // imports...
});

Dynamic import is also possible:

const wasmModule = await import.source('./module.wasm');
const instance = await WebAssembly.instantiate(wasmModule);

Additionally, Rspack has added module.rules[].phase for matching rules based on the import phase of a module. You can distinguish between evaluation for regular imports, defer for import defer, and source for Source Phase Imports, allowing you to configure different loaders, parser options, or module types for the same resource under different import methods.

Automatic Persistent Cache Cleanup

Rspack's persistent cache is isolated by version. When cache.version, cache-related configuration, or the Rspack version changes, Rspack creates a new cache version to avoid reusing incompatible caches. However, during long-term development, frequent branch switching, or when reusing working directories in CI, old cache versions can accumulate and consume disk space.

Rspack 2.1 introduces an automatic cleanup mechanism for the persistent cache, controlling the number of old versions retained in the cache directory via cache.maxAge and cache.maxVersions:

// rspack.config.mjs
export default {
  cache: {
    type: 'persistent',
    maxAge: 7 * 24 * 60 * 60,
    maxVersions: 3,
  },
};

When the number of cache versions exceeds the retention limit, or a version has not been accessed for a long time, Rspack will prioritize cleaning up older, less-accessed cache versions. This helps prevent the persistent cache directory from growing indefinitely while retaining recently reusable caches. For scenarios requiring fully manual cache management, you can set maxAge or maxVersions to Infinity to disable cleanup based on time or version count, respectively.

Output Optimization

pureFunctions Stabilization

Rspack 2.0 introduced the experimental pureFunctions for finer-grained tree shaking of side-effect-free function calls across module boundaries. After a period of iteration and validation, Rspack 2.1 enables this capability by default in production mode, eliminating the need to manually set experiments.pureFunctions: true.

This capability primarily covers two scenarios: you can add a /*#__NO_SIDE_EFFECTS__*/ annotation at the function definition, or you can mark side-effect-free functions via module.parser.javascript.pureFunctions. When the result of a call to a marked function is unused, Rspack will safely remove such calls during tree shaking.

For example, the join function below is declared as side-effect-free. If the return value of a call is not used, the call is automatically removed:

// utils.js
/*#__NO_SIDE_EFFECTS__*/
export function join(a, b) {
  return `${a}-${b}`;
}
// index.js
import { join } from './utils';

// Return value is unused, this call will be automatically removed
join('btn', 'primary');

If you wish to disable this analysis, you can disable experiments.pureFunctions:

// rspack.config.mjs
export default {
  experiments: {
    pureFunctions: false,
  },
};

See the tree shaking guide for more details.

Branch-Aware Dependency Pruning

Rspack 2.1 improves dependency analysis in scenarios involving inlined constants. When the condition of an if statement or ternary expression depends on an already-inlined boolean export, Rspack now associates the branch condition with the dependencies inside that branch. If it can subsequently determine that a branch will not be executed, the dependencies within that branch are marked as inactive, thus participating in tree shaking and chunk pruning.

// env.js
export const IS_DEV = false;
// index.js
import { IS_DEV } from './env';

if (IS_DEV) {
  import('./debug-tools');
} else {
  import('./app');
}

In the example above, IS_DEV can be inlined as false, so the ./debug-tools dependency in the if branch is no longer considered an active dependency. Compared to the previous behavior of retaining dependencies from both branches, this reduces the inclusion of unused modules in the output and avoids generating unnecessary dynamic import chunks for unreachable branches.

This optimization also supports simple boolean expressions composed of !, &&, ||, etc., as well as branch dependencies within ternary expressions. For code where the condition cannot be statically determined, Rspack retains its original behavior to ensure runtime semantics are unaffected.

Branch-Aware ESM Export Existence Detection

Rspack detects during compilation whether ESM imports access non-existent exports and issues warnings like export ... was not found. Previously, this detection could not understand runtime existence checks like if ("name" in ns). Therefore, even if code first checks whether an export exists, accesses within the branch could still produce false positives.

Rspack 2.1 enhances its analysis of in expressions on ESM namespaces. When an export access is guarded by the same in check, Rspack recognizes this branch condition and no longer reports a missing export warning for that access.

// index.js
import * as feature from './feature';

if ('debug' in feature) {
  feature.debug();
}

In the example above, if ./feature does not export debug, 'debug' in feature will return false at runtime, and feature.debug() inside the branch will not execute. Rspack now understands this and will no longer issue a missing export warning for this guarded access.

This detection also applies to branch conditions composed of !, &&, ||, and ternary expressions. It also supports namespace imports, named export namespace objects, and nested member access. For accesses not guarded by the same in check, Rspack will continue to report warnings, ensuring that genuine missing export issues are not hidden.

export const Value Binding Optimization

Rspack 2.1 streamlines the export code for ESM export const. In previous versions, Rspack would uniformly generate getter functions for ESM exports to maintain ESM live binding semantics:

__webpack_require__.d(__webpack_exports__, {
  value: () => value,
});

However, for export const in non-circular modules, the exported value does not change after module initialization, so there is no need to read it via a getter every time. Rspack 2.1 makes a judgment during production builds by considering circular module information: when it confirms that the current module is not in a circular dependency, export const is defined as a read-only value on the namespace object, thereby reducing generated code and runtime getter call overhead.

// Output illustration
// Before optimization: accessing namespace.value executes a getter function
__webpack_require__.d(__webpack_exports__, {
  value: () => value,
});

// Rspack 2.1: const exports in non-circular modules are directly defined as read-only values
// This means accessing namespace.value no longer requires executing a getter function
__webpack_require__.d(
  __webpack_exports__,
  {},
  {
    value: value,
  },
);
// constants.js
export const answer = 42;

const message = 'hello';
export { message };

export default 'default value';

The named const exports above, as well as constant values in default exports, can benefit from this optimization. For let, function exports, and const exports in circular modules, Rspack will retain the getter form to ensure correct semantics for mutable exports and circular dependency scenarios. We will continue to try to identify more stable exports based on reassignment analysis, allowing more scenarios to use a lighter value binding form.

Ecosystem

TanStack RSC Support

We are collaborating with the TanStack team to improve Rsbuild support for TanStack Start and have achieved milestones: TanStack Start has officially added Rsbuild support. Developers can now use Rsbuild to build TanStack Start applications, gaining access to framework capabilities including RSC.

Our core goal in the RSC direction is to provide general-purpose RSC build capabilities, allowing higher-level frameworks to integrate RSC based on their own routing, rendering, and server-side runtime solutions, reusing a unified build infrastructure.

If you want to try RSC within the Rspack technology stack, you can refer to:

Rsbuild

Rsbuild 2.1 has been released in sync with Rspack 2.1. See the Rsbuild 2.1 blog for more details.

Rslib

Rslib has added a fast type generation method based on isolatedDeclarations. By enabling dts.isolated, Rslib uses SWC's type generation capability during the Rspack build process to directly output type declaration files for TypeScript modules in the dependency graph.

// rslib.config.ts
import { defineConfig } from '@rslib/core';

export default defineConfig({
  lib: [
    {
      dts: {
        isolated: true,
      },
    },
  ],
});

This capability is suitable for daily build scenarios in Monorepos or multi-package libraries. You can split the generation of type declaration files and type checking into two steps:

Using the Rsbuild repository as an example, the time taken to generate type declaration files with different methods is as follows:

Method Time
TypeScript 6 9.7s
TypeScript 7 4.1s
Isolated Declarations 2.3s

For more details, refer to dts.isolated.

Rstest

Rstest 0.10 focuses on testing efficiency and stability, adding --changed / --related test filtering capabilities. It can run only the tests affected by source code changes, significantly shortening feedback time for large projects in local development and CI.

--changed automatically detects changed source files from the Git working directory, including unstaged, staged, and untracked files, and runs only the related tests:

rstest run --changed

You can also specify a commit or branch as the comparison range:

rstest run --changed=HEAD~1
rstest run --changed=origin/main

--related allows you to explicitly pass source files, running only tests that depend on these files. It also provides a --findRelatedTests alias compatible with Jest:

rstest run --related src/button.ts
rstest run --findRelatedTests src/button.ts

If you only want to preview the affected test files, you can use it with rstest list:

rstest list --changed --filesOnly
rstest list --related src/button.ts --filesOnly

Additionally, Rstest 0.10 includes the following improvements:

For more information, refer to the Rstest 0.10 blog.

Rslint

Built-in Rich Rules: Rslint has ported rules from ESLint core and community plugins such as @typescript-eslint, react, jsx-a11y, jest, promise, etc. Currently, it has over 400 built-in rules, with common rules available out of the box.

Compatible with ESLint Plugins: In addition to built-in rules, Rslint can now directly run rules from community ESLint plugins, working alongside native built-in rules. Simply mount the plugin with a custom prefix in the configuration. Diagnostics generated by the plugin are merged into the same report. Autofix can also be applied via --fix and the editor's source.fixAll, and behaves consistently in the CLI and VS Code extension.

// rslint.config.mjs
import examplePlugin from 'eslint-plugin-example';

export default [
  {
    files: ['**/*.ts'],
    plugins: { example: examplePlugin },
    rules: {
      'example/some-rule': 'error',
    },
  },
];

For more usage and current limitations, refer to the ESLint Plugin Compatibility Guide.

Rspress

Rspress now offers more Agent Skills, for example:

Below is a theme generated using rspress-custom-theme:

Rspress Custom Theme Skill

These skills can be installed directly into an existing project or selected during project initialization. For more information, refer to Rspress - AI Guide.

Rsdoctor

Rsdoctor has added AI analysis capabilities in GitHub Actions. When there are size changes in the build analysis report, it can analyze degradation issues in conjunction with the current project's build data, assisting in locating performance bottlenecks, output size growth, and optimization directions.

Rsdoctor AI Action Analysis Details

rspack-merge

We have released rspack-merge, an npm package for merging Rspack configurations. It covers needs from basic configuration merging to rule-level merging of loaders and plugins.

// rspack.config.ts
import { defineConfig } from '@rspack/cli';
import { merge } from 'rspack-merge';

const sharedConfig = defineConfig({
  // ...
});

const serverConfig = merge(sharedConfig, {
  // ...
});

const clientConfig = merge(sharedConfig, {
  // ...
});

export default [serverConfig, clientConfig];

rspack-merge is written in TypeScript, publishes modern ESM build artifacts, and maintains zero runtime dependencies, keeping configuration merging simple, reliable, and lightweight.