---
title: "Rspack: An Engineer’s Approach to Build System Innovation"
date: 2025-01-31T14:58:23.000Z
canonical: https://www.ekino.fr/publications/rspack-an-engineers-approach-to-build-system-innovation/
---

A Pragmatic Look at Rspack’s Features, Performance, and Benefits

![](https://www.ekino.fr/media/original/0000/00/8ddac421-2238-be7b-a0b4-bde3f45cddac.jpg)
*Image from [Unsplash](https://unsplash.com/fr/photos/experience-de-la-technologie-abstraite-du-code-de-programmation-du-developpeur-de-logiciels-et-du-script-informatique-ltpb_WinC3Y)*

### Introduction

#### What is this about?

The objective of this study is to assess the feasibility of adopting Rspack as a build system for some [**ekino-France**](https://medium.com/u/df10e8b05bea)** React projects**, evaluating its advantages, limitations, and suitability compared to Webpack, Vite, and SWC. The focus includes key performance metrics, migration complexity, compatibility with current workflows, and long-term maintainability.

This study is a continuation of [Beyond Webpack: esbuild, Vite, Rollup, SWC, and Snowpack](https://medium.com/ekino-france/beyond-webpack-esbuild-vite-rollup-swc-and-snowpack-97911a7175cf), where we examined modern Webpack alternatives for React projects. We previously selected Vite for CSR (Client Side Rendering) projects due to its speed and efficiency (esbuild + Rollup) and Next.js for SSR (Server Side Rendering) projects (SWC + Webpack), optimizing both CSR and SSR workflows.

However, **legacy CSR projects using Webpack and non-ESM modules** pose challenges **where Vite may not be suitable**. The study examines whether Rspack is a **viable alternative**, balancing performance enhancements with seamless migration while addressing Webpack’s complexity.

Key areas of evaluation include:

- **Performance Evaluation**: Build speed, cold start times, incremental compilation, and HMR performance.
- **Compatibility Analysis**: Integration with Webpack configurations, existing plugins, and loaders.
- **Migration Feasibility**: Effort required to transition from Webpack, potential blockers, and configuration adjustments.
- **Ecosystem Maturity**: Plugin and loader ecosystem, release stability, and adoption trends.
- **Use Case Alignment**: Identifying project types at ekino (e.g., SPAs, SSR applications, micro-frontends) that would benefit the most.
- **Potential Risks**: Limitations, missing features, or drawbacks affecting adoption.
- **Long-Term Viability**: Rspack’s roadmap, maintainability, and industry alignment.

This study serves as a **technical and pragmatic assessment** to determine whether Rspack is a viable alternative for some of ekino’s legacy CSR projects.

#### POC Application: Benchmarking Webpack vs. Rspack

To compare Rspack fairly, we will use the same [POC application](https://github.com/helabenkhalfallah/frontend-beyond-webpack/tree/main) tested with Webpack, esbuild, Rollup, and SWC. This ensures a consistent evaluation of performance, build speed, and developer experience.

While Webpack provides powerful features like declarative configuration, extensibility, HMR, code splitting, tree shaking, and module federation, its build performance and complex migration challenges remain key drawbacks.

Our goal is to assess whether Rspack can retain Webpack’s advantages while offering better efficiency. For reference, we will use the following Webpack benchmarks:

🔳 **Bundle analyze:**

```
// webpack (gzip)
All (170.73 KB)
webpack-frontend.js (141.08 KB)
webpack-frontend-389.chunk.js (15.88 KB)
webpack-frontend-595.chunk.js (7.15 KB)
webpack-frontend-930.chunk.js (4.52 KB)
webpack-frontend-615.chunk.js (1.46 KB)
webpack-frontend-553.chunk.js (646 B)

// webpack(brotli)
All (147.87 KB)
webpack-frontend.js (121.38 KB)
webpack-frontend-389.chunk.js (14.32 KB)
webpack-frontend-595.chunk.js (6.3 KB)
webpack-frontend-930.chunk.js (4.05 KB)
webpack-frontend-615.chunk.js (1.26 KB)
webpack-frontend-553.chunk.js (562 B)

// webpack + swc (gzip)
All (169.06 KB)
swc-frontend.js (140.07 KB)
swc-frontend-389.chunk.js (15.88 KB)
swc-frontend-431.chunk.js (7.16 KB)
swc-frontend-930.chunk.js (4.52 KB)
swc-frontend-671.chunk.js (832 B)
swc-frontend-887.chunk.js (632 B)

// webpack + swc (brotli)
All (146.38 KB)
swc-frontend.js (120.42 KB)
swc-frontend-389.chunk.js (14.32 KB)
swc-frontend-431.chunk.js (6.33 KB)
swc-frontend-930.chunk.js (4.06 KB)
swc-frontend-671.chunk.js (727 B)
swc-frontend-887.chunk.js (563 B)
```

🔳 **The required time for build is:**

```
⚡ Done in 289ms // ESBuild
created dist in 8.2s // Rollup
webpack 5.84.1 compiled with 2 warnings in 3365 ms (3,3365 s) // webpack
webpack 5.84.1 compiled with 2 warnings in 3368 ms // webpack + SWC
```

🔳 **Lighthouse results for production mode:**

```
// webpack
Performance: 95, Best Practices: 100

// ESBuild
Performance: 96, Best Practices: 100

// Rollup
Performance: 100, Best Practices: 100

// ESBuild
Performance: 99, Best Practices: 100
```

By maintaining the same test conditions, we can **fairly compare Rspack against Webpack** and determine if it can offer tangible improvements. Let’s see if **Rspack can do better!** 🚀

### Understanding Rspack’s Internal Architecture

#### Tracing Rspack’s Execution from index.js to bundle.js

We will trace how Rspack compiles an index.js file into bundle.js, integrating the links to key files involved in the process.

🔵** Step 1: CLI Entry Point **[rspack.js](https://github.com/web-infra-dev/rspack/blob/main/packages/rspack-cli/bin/rspack.js)

```
const { RspackCLI } = require("../dist/index");

async function runCLI() {
  const cli = new RspackCLI();
  await cli.run(process.argv);
}

runCLI();
```

🔸 **What Happens Here?**

- **User runs the Rspack CLI** (rspack build or rspack serve).
- The **entry script** (rspack.js) is executed.
- It imports RspackCLI, the command-line interface module.
- It runs runCLI(), which processes command-line arguments.

👉 **Key Takeaway:** rspack.js is the **entry point** of Rspack’s CLI, initializing and running commands.

**🔵 Step 2: Parsing Commands (****build, ****serve, etc.) **[cli.ts](https://github.com/web-infra-dev/rspack/blob/main/packages/rspack-cli/src/cli.ts)

```
export class RspackCLI {
  async run(argv: string[]) {
    this.program.strictCommands(true).strict(true);
    this.registerCommands();
    await this.program.parseAsync(hideBin(argv));
  }

  async registerCommands() {
    const builtinCommands = [new BuildCommand(), new ServeCommand()];
    for (const command of builtinCommands) {
      command.apply(this);
    }
  }
}
```

🔸 **What Happens Here?**

- **Command Parsing**: rspack-cli registers available commands (build, serve, etc.).
- **Command Dispatching**: Calls the appropriate command handler (build.ts for rspack build).
- **Middleware Processing**: Environment variables are normalized.

👉 **Key Takeaway:** The **CLI** reads user input (rspack build), validates it, and executes the corresponding command.

**🔵 Step 3: Handling ( ****rspack build )**[build.ts](https://github.com/web-infra-dev/rspack/blob/main/packages/rspack-cli/src/commands/build.ts)

```
const compiler = await cli.createCompiler(
  rspackOptions,
  "build",
  errorHandler
);

compiler.run((error, stats) => {
  compiler.close(closeErr => {
    if (closeErr) logger.error(closeErr);
    errorHandler(error, stats);
  });
});
```

🔸 **What Happens Here?**

- **Reads user’s configuration (****rspack.config.js)**.
- **Creates a compiler instance** using createCompiler().
- **Runs the compiler**, triggering the bundling process.

👉 **Key Takeaway: **This initiates the compilation process by **creating and running the compiler**.

**🔵 Step 4: Creating the Compiler **[rspack.ts](https://github.com/web-infra-dev/rspack/blob/main/packages/rspack/src/rspack.ts)

```
function createCompiler(userOptions: RspackOptions): Compiler {
  const options = getNormalizedRspackOptions(userOptions);
  applyRspackOptionsBaseDefaults(options);
  assert(!isNil(options.context));
  const compiler = new Compiler(options.context, options);

  new NodeEnvironmentPlugin({
    infrastructureLogging: options.infrastructureLogging
  }).apply(compiler);

  if (Array.isArray(options.plugins)) {
    for (const plugin of options.plugins) {
      if (typeof plugin === "function") {
        plugin.call(compiler, compiler);
      } else if (plugin) {
        plugin.apply(compiler);
      }
    }
  }
  applyRspackOptionsDefaults(compiler.options);
  new RspackOptionsApply().process(compiler.options, compiler);
  return compiler;
}
```

🔸 **What Happens Here?**

- **Parses & normalizes user options** (e.g., entry, output, loaders).
- **Initializes a ****Compiler instance**.
- **Applies plugins** like RspackOptionsApply for transformations.
- **Triggers compilation hooks** (environment, afterEnvironment, initialize).

```
if (Array.isArray(options.plugins)) {
 for (const plugin of options.plugins) {
  if (typeof plugin === "function") {
   plugin.call(compiler, compiler);
  } else if (plugin) {
   plugin.apply(compiler);
  }
 }
}
```

👉 **Key Takeaway: **Plugins **register their modifications** here, affecting the compilation process.

**🔵 Step 5: Running Plugins Before Compilation** [Compiler.ts](https://github.com/web-infra-dev/rspack/blob/main/packages/rspack/src/Compiler.ts)

```
class Compiler {
  compile(callback) {
    this.hooks.beforeCompile.callAsync({}, err => {
      if (err) return callback(err);
      this.hooks.compile.call({});
      this.hooks.afterCompile.call({});
      callback();
    });
  }
}
```

🔸 **What Happens Here? **Hooks like beforeCompile, compile, and afterCompile **allow plugins to modify the AST (Abstract Syntax Tree).**

👉 **Key Takeaway: **Plugins can **optimize, transform, or modify** the compilation output.

**🔵 Step 6: Running Loaders (****index.js -> Transformed Code) **[loader-runner/index.ts](https://github.com/web-infra-dev/rspack/blob/main/packages/rspack/src/loader-runner/index.ts)

```
async function runLoaders(compiler, context) {
  const loaderContext = { loaders: context.loaderItems.map(LoaderObject.__from_binding) };
  while (loaderContext.loaderIndex < loaderContext.loaders.length) {
    const currentLoader = loaderContext.loaders[loaderContext.loaderIndex];
    await loadLoaderAsync(currentLoader);
    const fn = currentLoader.normal;
    [context.content] = await runSyncOrAsync(fn, loaderContext, [context.content]);
  }
  return context;
}
```

🔸 **What Happens Here?**

- **Runs each loader** in sequence (e.g., Babel for JS, PostCSS for CSS).
- **Transforms the source code**.
- **Passes processed content to the bundler**.

👉 **Key Takeaway:** Loaders **transform files** before bundling (e.g., TypeScript → JavaScript).

**🔵 Step 7: Running Built-in Plugins** [builtin-plugin/index.ts](https://github.com/web-infra-dev/rspack/blob/main/packages/rspack/src/builtin-plugin/index.ts)

🔸 **Key Built-in Plugins:**

1. **EntryPlugin** → Handles entry points.
2. **DefinePlugin** → Replaces global constants.
3. **SplitChunksPlugin** → Optimizes chunk loading.
4. **CssModulesPlugin** → Handles CSS Modules.
5. **JsLoaderRspackPlugin** → Runs JavaScript transformations.

```
class EntryPlugin {
 apply(compiler) {
  compiler.hooks.entryOption.tap("EntryPlugin", (context, entry) => {
   // Modify entry points dynamically
   return true;
  });
 }
}
```

👉 **Key Takeaway:** Plugins **inject features into the compilation pipeline**.

**🔵 Step 7: Chunking & Bundling (****JavascriptModulesPlugin.ts)** [JavascriptModulesPlugin.ts](https://github.com/web-infra-dev/rspack/blob/main/packages/rspack/src/builtin-plugin/JavascriptModulesPlugin.ts)

```
class JavascriptModulesPlugin {
 apply(compiler) {
  compiler.hooks.compilation.tap("JavascriptModulesPlugin", compilation => {
   compilation.hooks.renderChunk.tap("JavascriptModulesPlugin", chunk => {
    return `(()=>{ ${chunk.modules.map(m => m.code).join("n")} })()`;
   });
  });
 }
}
```

🔸 **What Happens Here?**

- Rspack **chunks files into separate modules**.
- Applies **tree shaking** & optimizations.
- Converts modules into **a single JavaScript bundle**.

👉 **Key Takeaway: Optimizations** like **tree shaking** are done here.

**🔵 Step 8: Emitting ****bundle.js (****Compiler.ts) **[Compiler.ts](https://github.com/web-infra-dev/rspack/blob/main/packages/rspack/src/Compiler.ts)

```
class Compiler {
 emitAssets(callback) {
  this.hooks.emit.callAsync(this.assets, err => {
   if (err) return callback(err);
   this.outputFileSystem.writeFile(
    this.outputPath,
    this.assets["bundle.js"].source(),
    callback
   );
  });
 }
}
```

🔸 **What happens here?**

- **Plugins** optimize the output.
- The final bundle is **written to disk**.

👉 **Key Takeaway: **The final bundle.js is **saved and ready to execute**.

**🔵 **In summary,** **Rspack’s build pipeline consists of:

- **CLI Execution** (rspack.js) → Initializes CLI.
- **Command Handling** (cli.ts) → Dispatches rspack build.
- **Compiler Creation** (rspack.ts) → Initializes plugins.
- **Compilation Lifecycle** (Compiler.ts) → Runs compilation hooks.
- **Loaders** (loader-runner/index.ts) → Transforms source files.
- **Chunking & Optimization** (JavascriptModulesPlugin.ts) → Bundles modules.
- **Emission** (Compiler.ts) → Writes final bundle.js.

Each step integrates Rust-based performance optimizations while maintaining Webpack compatibility.

#### Core Components of Rspack

Rspack is a high-performance **Rust-based** bundler designed to be **compatible with Webpack** while **improving** build speed, memory efficiency, and parallel execution.

![](https://www.ekino.fr/media/original/0000/00/1e9935ad-a964-0f22-62f4-6efd3d1c2dd7.jpg)
*[Rspack architecture diagram](https://blog.ijust.cc/quick-review-dd-rspack)*

Below, we outline its **key components** and their roles in the bundling process.

**🔵 **[**Plugins**](https://rspack.dev/plugins/index)** :**

- Plugins **extend the compiler’s lifecycle** by modifying various build phases.
- They are responsible for **optimizations, transformations, and additional build features** (e.g., defining global constants, code splitting, and HMR).
- Rspack **inherits **[**Webpack’s plugin system**](https://rspack.dev/plugins/webpack/index), ensuring smooth migration from Webpack.
- [Built-in plugins](https://rspack.dev/plugins/webpack/index) include [**EntryPlugin**](https://rspack.dev/plugins/webpack/entry-plugin)**, **[**DefinePlugin**](https://rspack.dev/plugins/webpack/define-plugin)**, **[**SplitChunksPlugin**](https://rspack.dev/plugins/webpack/split-chunks-plugin)**, and **[**HotModuleReplacementPlugin**](http://rspack.dev/plugins/webpack/hot-module-replacement-plugin).

**🔵 **[**Loaders**](https://rspack.dev/guide/features/loader#using-built-in-loader)** :**

- Loaders **process and transform non-JavaScript assets** before they enter the bundling pipeline.
- Loaders in Rspack operate in **two modes**: the **pitching phase**, which can alter the execution order or skip loaders entirely, and the **normal phase**, where the actual transformation of module content occurs.
- Supports **TypeScript, CSS, SCSS, images, and more**, similar to Webpack but with **better parallel execution**.
- Loaders **execute in parallel** via Rust’s **multi-threaded architecture**, reducing **bottlenecks**.
- **Supports both CommonJS & ESM natively**, unlike Rollup, which **prioritizes ESM**.

**🔵 Rust-Based Compilation Core:**

One of Rspack’s biggest advantages is its Rust-powered architecture, making it significantly faster and more memory-efficient than Webpack.

- **Multi-threading:** Unlike Webpack’s **single-threaded model**, Rspack **leverages multi-core CPUs** for **parallel processing**.
- **Lightning-fast parsing & transpilation:** Uses **SWC** instead of **Babel**, making JavaScript/TypeScript **~20x faster** to compile.
- **Reduced memory overhead:** Rust eliminates the need for **garbage collection (GC)**, leading to **better stability and lower memory usage**.

🔵 **Default Optimization:**

Rspack provides **built-in optimizations** to enhance performance and reduce bundle size:

- [**Tree Shaking**](https://rspack.dev/guide/optimization/tree-shaking): Efficiently removes dead code, minimizing output.
- [**Code Splitting**](https://rspack.dev/guide/optimization/code-splitting): Supports SplitChunksPlugin and **entry-based chunking**, enabling optimized lazy loading.
- [**Prefetching & Preloading**](https://rspack.dev/guide/optimization/code-splitting#prefetchingpreloading-modules): Uses inline directives similar to Webpack for smarter resource loading.
- [**Minification**](https://rspack.dev/guide/optimization/production#minification): Leverages **SwcJsMinimizerRspackPlugin** for JavaScript and **LightningCssMinimizerRspackPlugin** for CSS, ensuring optimized production builds.

These optimizations improve performance, reduce load times, and keep bundles lightweight **by default**.

**🔵 **[**Hot Module Replacement (HMR)**](https://rspack.dev/api/runtime-api/hmr)** :**

- Just like Webpack, Rspack **enables live updates** to JavaScript & CSS **without requiring a full page reload**.
- Thanks to Rust’s **parallel execution**, HMR is **faster and more efficient**, making **real-time development smoother**.

**🔵 **[**Incremental Compilation in Rspack**](https://github.com/orgs/web-infra-dev/discussions/24)** :**

Rspack optimizes Hot Module Replacement (HMR) with **affected-based incremental compilation**, **reducing update complexity** from [**O(project) to O(change)**](https://github.com/orgs/web-infra-dev/discussions/24) by re-executing only affected tasks.

- **Pass-Based Bundler with Early Cutoff**: Unlike Webpack, Rspack introduces a **Rebuilder** to track dependencies and skip redundant processing.
- **Minimizing Redundant Work**: It detects changes at different stages and updates only impacted tasks.
- **Inspired by Self-Adjusting Computation**: Ensures efficient propagation of updates through the dependency graph.

This results in **faster rebuilds and highly efficient HMR**, focusing only on modified parts of the project.

![](https://www.ekino.fr/media/original/0000/00/39af83b3-6c29-9515-0917-9353ff2ccb4c.png)
*[Building types](https://github.com/orgs/web-infra-dev/discussions/24)*

![](https://www.ekino.fr/media/original/0000/00/a54532b3-72b2-ea81-adc5-3bcdac11ff88.png)
*[Bundlers comparaison](https://github.com/orgs/web-infra-dev/discussions/24)*

🔵 [**Cache Optimization**](https://rspack.dev/blog/announcing-1-2#persistent-cache)**:**

Rspack speeds up builds by caching **snapshots** and **intermediate products**, reusing them in subsequent builds to **reduce redundant work** and improve performance.

- **Persistent Cache (Experimental)**: Introduced in **Rspack v1.2**, this feature **stores and reuses compilation artifacts** to accelerate hot startup times.
- **Performance Gains**: Can improve build speed by **up to 250%**, especially in large projects.

⚠️ **Note**: Persistent cache is still **experimental** and currently applies only to the **make stage** of the build process.

![](https://www.ekino.fr/media/original/0000/00/7d1612ad-4514-9881-c6b9-4bd0722cf6dc.png)
*[Persistent cache performance gains](https://rspack.dev/blog/announcing-1-2#persistent-cache)*

🔵 [**Module Federation**](https://rspack.dev/blog/module-federation-added-to-rspack)** **: Module Federation enables JavaScript applications to share code dynamically, supporting [micro-frontends](https://micro-frontends.org/) and independent team workflows. **Rspack provides native support** with performance improvements over webpack.

🔸 **Progressive Migration: **Rspack’s federation runtime is **compatible with ****webpack**, allowing both to share dependencies at runtime. This enables a smooth transition without a full rebuild.

🔸 **Rspack Module Federation Versions:**

- **v2.0 (Advanced)** — Type hints, Chrome DevTools, preloading, and runtime plugins.
- **v1.5 (Built-in)** — Adds runtime plugin support while maintaining Webpack compatibility.
- **v1.0 (Legacy)** — Webpack-compatible but deprecated.

Rspack’s Module Federation delivers **faster, scalable micro-frontends** while staying **webpack-compatible**.

Now that we’ve explored Rspack’s architecture and core features, it’s time to put theory into practice — first, we’ll create a new Rspack application from scratch, and then we’ll dive into migrating an existing webpack project to Rspack.

### Experimenting with Rspack

#### New application with Rspack: development mode

Rspack offers a Webpack-compatible configuration while significantly improving build performance. If you’re familiar with Webpack, transitioning to Rspack will feel intuitive.

To generate a new Rspack project with the necessary files (rspack.config.mjs, index.html, etc.), run:

```
pnpm create rspack@latest
```

Like Webpack, Rspack uses an entry field to define the main file where the bundling process begins:

```
entry: {
    main: './src/index.js',
},
```

Rspack provides the **HtmlRspackPlugin** (similar to Webpack’s HtmlWebpackPlugin) to generate an HTML file and inject bundled assets:

```
plugins: [
    new rspack.HtmlRspackPlugin({
       template: './index.html',
    }),
    isDev ? new RefreshPlugin() : null,
].filter(Boolean),
```

Rspack **natively supports SWC** as the default loader (builtin:swc-loader), removing the need for Babel in most cases.

**For React 18**: We only need **SWC**, with no Babel plugin.

**For React 19**: Since React 19 requires the **React Compiler**, we must use Babel alongside SWC:

```
      
...
{
        test: /.jsx$/,
        use: [
          {
            loader: 'builtin:swc-loader',
            options: {
              // SWC options for JSX
            },
          },
          { loader: 'babel-loader' },
        ],
      },
    ],
...
```

babel.config.js file:

```
const ReactCompilerConfig = {
  /* ... */
};

module.exports = function () {
  return {
    plugins: [
      ['babel-plugin-react-compiler', ReactCompilerConfig], // must run first!
      '@babel/plugin-syntax-jsx',
    ],
  };
};
```

💡 While relying on Babel for React 19 may not be ideal, it remains necessary due to the compiler being exposed via a Babel plugin.

Since we’re using **React Router**, we need to enable history fallback to support client-side routing (rspack.config.mjs ):

```
devServer: {
    historyApiFallback: true,
},
```

To run and build the project, add the following scripts to package.json:

```
"start": "cross-env NODE_ENV=development rspack serve",
"build": "cross-env NODE_ENV=production rspack build",
```

Below are the results of the local launch:

```
<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: http://localhost:9001/
<i> [webpack-dev-server] On Your Network (IPv4): http://10.180.152.212:9001/
<i> [webpack-dev-server] On Your Network (IPv6): http://[fe80::1]:9001/
<i> [webpack-dev-server] Content not from webpack is served from '/Users/hela.ben-khalfallah/Desktop/github_workspace/frontend-beyond-webpack/rspack-app/public' directory
<i> [webpack-dev-server] 404s will fallback to '/index.html'
●  ━━━━━━━━━━━━━━━━━━━━━━━━━ (100%) emitting after emit                                                                                                                                                
Rspack compiled successfully in 258 ms
```

![](https://www.ekino.fr/media/original/0000/00/2cd7bf4a-bf7a-aaab-fe5f-2c5aa5a5ada5.png)
*Rspack lighthouse analysis — local (Image by the author)*

You can find [here](https://github.com/helabenkhalfallah/frontend-beyond-webpack/tree/main/rspack-app) the full Rspack application.

Now, let’s move on to **Production Mode**! 🌟

#### New application with Rspack: Production mode

Rspack’s CLI provides built-in bundle analysis via the --analyze flag, leveraging [webpack-bundle-analyzer](https://www.npmjs.com/package/webpack-bundle-analyzer):

```
"analyze": "cross-env NODE_ENV=production rspack build --analyze",
```

By default, Rspack compresses output using **Gzip:**

![](https://www.ekino.fr/media/original/0000/00/d1098f37-47b4-8bf6-86ff-73d1e3f96a0c.png)
*Bundle analyze with Gzip compression — rspack (Image by the author)*

Rspack does not have a dedicated Brotli plugin, but we can reuse Webpack’s approach. Since it maintains Webpack compatibility, Brotli compression can be added using compression-webpack-plugin:

```
import zlib from 'zlib';
import CompressionPlugin from 'compression-webpack-plugin';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer-brotli';

...

 plugins: [
  ...
    !isDev &&
     new CompressionPlugin({
      filename: '[path][base].br',
      algorithm: 'brotliCompress',
      test: /.(js|css|html|svg)$/,
      compressionOptions: {
       params: {
        [zlib.constants.BROTLI_PARAM_QUALITY]: 11,
       },
      },
      threshold: 10240,
      minRatio: 0.8,
     }),
    !isDev &&
     new BundleAnalyzerPlugin({
      analyzerPort: 9003,
     }),
...
```

This perfectly works and the result is as follow:

![](https://www.ekino.fr/media/original/0000/00/686259de-bac6-6db6-bf98-b66c3d59af8e.png)
*Bundle analyze with Brotli compression — rspack (Image by the author)*

![](https://www.ekino.fr/media/original/0000/00/91751255-a1bf-1713-7650-912283706907.png)
*Bundle analyze with Gzip compression — rspack (Image by the author)*

Comparing the output size to full Webpack application:

```
// webpack (gzip)
All (170.73 KB)
webpack-frontend.js (141.08 KB)
webpack-frontend-389.chunk.js (15.88 KB)
webpack-frontend-595.chunk.js (7.15 KB)
webpack-frontend-930.chunk.js (4.52 KB)
webpack-frontend-615.chunk.js (1.46 KB)
webpack-frontend-553.chunk.js (646 B)

// webpack(brotli)
All (147.87 KB)
webpack-frontend.js (121.38 KB)
webpack-frontend-389.chunk.js (14.32 KB)
webpack-frontend-595.chunk.js (6.3 KB)
webpack-frontend-930.chunk.js (4.05 KB)
webpack-frontend-615.chunk.js (1.26 KB)
webpack-frontend-553.chunk.js (562 B)


// rspack (gzip)
All (169.39 KB)
main.js (140.35 KB)
410.js (15.72 KB)
62.js (6.77 KB)
764.js (4.5 KB)
207.js (868 B)
100.js (643 B)
220.js (596 B)

// rspack(brotli)
All (147.11 KB)
main.js (121.16 KB)
410.js (14.16 KB)
62.js (5.95 KB)
764.js (4.04 KB)
207.js (759 B)
100.js (566 B)
220.js (513 B)
```

The size difference is minimal, but **Rspack achieves significantly faster build times**:

```
⚡ Done in 289ms // ESBuild
created dist in 8.2s // Rollup
webpack 5.84.1 compiled with 2 warnings in 3365 ms (3,3365 s) // webpack
webpack 5.84.1 compiled with 2 warnings in 3368 ms // webpack + SWC
Rspack compiled with 2 warnings in 1.02 s // Rspack
```

Rspack’s Rust-based architecture significantly **reduces build times** while maintaining bundle size optimizations.

To further analyze the bundle and build performance, we can use Rsdoctor:

```
pnpm add @rsdoctor/rspack-plugin -D
```

Then, add the plugin to rspack.config.mjs:

```
import { RsdoctorRspackPlugin } from '@rsdoctor/rspack-plugin';

...

 plugins: [
    ...

    // Only register the plugin when RSDOCTOR is true, as the plugin will increase the build time.
    process.env.RSDOCTOR &&
     new RsdoctorRspackPlugin({
      // plugin options
     }),
    ...
 ].filter(Boolean),

...
```

Run:

```
"scripts": {
  ...
  "doctor": "RSDOCTOR=true pnpm run build"
  ...
```

This generates a **detailed diagnostic report** including project configuration, compilation stats, and performance insights:

![](https://www.ekino.fr/media/original/0000/00/9a0199db-31eb-12fd-ad03-5cf5b5fa6a81.png)
*Rsdoctor report — Home Page (Image by the author)*

![](https://www.ekino.fr/media/original/0000/00/cfa05585-1d07-c247-8514-59ae2f35d213.png)
*Rsdoctor report — Bundle Details Page (Image by the author)*

Now, let’s attempt to deploy this application in [Netlify](https://frontend-beyond-webpack.netlify.app/product_list) and see the results.

```

<!doctype html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Rspack + React</title>
    <script defer src="main.js"></script>
  </head>
  <body>
    <div id="rspack-frontend-root"></div>
  </body>
</html>
```

![](https://www.ekino.fr/media/original/0000/00/739e2bc5-6a32-7c0e-f714-1bbb40cb168d.png)
*Lighthouse audit — rspack (Image by the author)*

The **Lighthouse audit** confirms that **Rspack delivers performance on par with Webpack**.

You can find [here](https://github.com/helabenkhalfallah/frontend-beyond-webpack/tree/main/rspack-app) the full Rspack application.

#### Migrating legacy application from Webpack to Rspack

You can find the [**source code**](https://github.com/helabenkhalfallah/frontend-beyond-webpack/tree/a2248040fc72c58656b06c98ff55f42bc974e6a6/webpack-to-rspack-app) of the existing Webpack application **before migration** to Rspack.

Now, let’s go step by step through the migration process.

1️⃣ **Remove Webpack Configuration: **Since Rspack maintains compatibility with Webpack, we start by [removing](https://github.com/helabenkhalfallah/frontend-beyond-webpack/commit/acac3404e1eb96a9eaf968234e5b4822b4179248)** the Webpack configuration folder** and [**dependencies**](https://github.com/helabenkhalfallah/frontend-beyond-webpack/commit/746f1d158b50d7517c1de9c11e6a028a8dfc563f) we no longer need:

- **Remove Babel Dependencies** (for React 18).
- **Uninstall Webpack & Loaders** (keeping only Brotli-related Webpack plugins).

![](https://www.ekino.fr/media/original/0000/00/4065f54c-0fb0-052a-e7b9-220a26dfe62e.png)
*Webpack configuration folder (Image by the author)*

![](https://www.ekino.fr/media/original/0000/00/8b6b1e7f-92d4-0b0a-9979-e27cf8724f15.png)
*[Dependencies cleaning](https://github.com/helabenkhalfallah/frontend-beyond-webpack/commit/746f1d158b50d7517c1de9c11e6a028a8dfc563f) (Image by the author)*

2️⃣ **Update Project Configuration: **Next, we [copy](https://github.com/helabenkhalfallah/frontend-beyond-webpack/commit/4c88e11b1c7dd818efbea753218481688a52bb27) the **Rspack configuration** into the legacy project, ensuring compatibility while updating any required paths.

3️⃣ **Update Build Scripts: **Replace Webpack-related [scripts](https://github.com/helabenkhalfallah/frontend-beyond-webpack/commit/a8b2f570ba2ffc1f2897157b9b5388e9123d0337) with their Rspack equivalents:

🚀 **Before (****webpack scripts)**

```
"start": "webpack serve -c webpack/webpack.dev.config.js --progress --color",
"build": "webpack -c webpack/webpack.prod.config.js --progress --color",
"analyze": "webpack -c webpack/webpack.debug-prod.config.js --progress --color",
```

🛠️ **After (****rspack scripts)**

```
"start": "cross-env NODE_ENV=development rspack serve -c ./rspack/rspack.config.dev.mjs",
"build": "cross-env NODE_ENV=production rspack build -c ./rspack/rspack.config.prod.mjs",
"analyze": "BUNDLEANALYZE=true cross-env NODE_ENV=production rspack build -c ./rspack/rspack.config.prod.debug.mjs --analyze",
"doctor": "RSDOCTOR=true rspack build -c ./rspack/rspack.config.prod.debug.mjs",
```

4️⃣ **Install Required Dependencies: **To complete the migration, install **Rspack dependencies**:

```
  "devDependencies": {
      ...
      "@rsdoctor/rspack-plugin": "^0.4.13",
      "@rspack/cli": "=1.2.2",
      "@rspack/core": "=1.2.2",
      "@rspack/plugin-react-refresh": "1.0.0",
      "cross-env": "=7.0.3"
      ...
```

5️⃣ **Validate Migration Success: **Finally, we **test all scripts** to ensure the migrated application functions correctly:

✔️ **Start**: OK (No regression or exceptions)
✔️ **Build**: OK (Brotli & Gzip correctly generated)
✔️ **Analyze**: OK (Bundle size remains unchanged)
✔️ **Doctor**: OK (No issues detected)

The migration process was seamless! With minimal changes, Rspack delivered **faster build times** while maintaining full compatibility. 🚀

#### Review and summary

Based on our initial evaluation objectives, here’s how **Rspack** performed:

1️⃣ Performance Evaluation:

- **Build speed**: Rspack consistently outperforms Webpack (~3x faster in most cases).
- **Cold start & incremental builds**: Faster than Webpack, but persistent caching is still evolving.
- **HMR performance**: Affected-based updates make HMR more efficient.

**2️⃣ Compatibility Analysis:**

- **Supports most Webpack configurations** with minor path and script updates.
- **Works with existing plugins/loaders**, except for some needing Webpack-specific APIs.
- **Compression**: Gzip is built-in; Brotli requires compression-webpack-plugin.

**3️⃣ Migration Feasibility:**

- **Minimal effort required**: Removing Webpack dependencies and adjusting config paths is enough.
- **No major regressions**: Bundle output remains comparable to Webpack.
- **Tooling adjustments**: Some diagnostic tools (e.g., @rsdoctor/rspack-plugin) help maintain parity.

Having validated Rspack’s **core performance, compatibility, and migration process**, the next phase will focus on **ecosystem maturity** (plugins, loaders, debugging tools), **potential risks** (missing features, debugging challenges), and **use case suitability** for different project types.

### Evaluating Rspack for ekino Projects

#### **Advantages of Adopting Rspack**

- **Significant Build Speed Improvements** — Rspack outperforms Webpack in both cold starts and incremental builds, reducing developer wait times.
- **Rust-Based Performance Optimizations** — Efficient memory management and multi-threading result in lower resource usage.
- **Seamless Webpack Compatibility** — Existing Webpack projects can migrate progressively without full rewrites.
- **Built-in Optimization Features** — Tree shaking, code splitting, and caching improve runtime performance.
- **Module Federation Support** — Enables scalable micro-frontends and interoperability with Webpack-based applications.
- **First-Class CSS Support** — Rspack provides built-in CSS handling with **native CSS support**, CssExtractRspackPlugin for optimized extraction, and [compatibility](https://rspack.dev/guide/tech/css) with PostCSS, Sass, Less, and Tailwind CSS, ensuring a seamless styling experience.

#### **Potential Limitations and Risks**

- **Ecosystem Maturity** — While Rspack offers strong Webpack compatibility, some Webpack plugins and loaders may not yet be [fully supported](https://rspack.dev/plugins/webpack/index), requiring [additional validation](https://rspack.dev/guide/migration/webpack). Many widely used plugins such as html-webpack-plugin, mini-css-extract-plugin, and copy-webpack-plugin have direct replacements (html-rspack-tags-plugin, CssExtractRspackPlugin, and CopyRspackPlugin, respectively). Additionally, certain Webpack plugins are only partially compatible (e.g., image-minimizer-webpack-plugin, webpack-assets-manifest), while others remain [incompatible](https://rspack.dev/guide/compatibility/plugin) (e.g., @cypress/webpack-preprocessor, circular-dependency-plugin, pnp-webpack-plugin).
- **Community Adoption and Support** — Compared to Webpack, Rspack has a smaller community and fewer third-party integrations, which could impact troubleshooting and long-term support.
- **SSR Support is **[**Not Fully Mature**](https://rspack.dev/blog/announcing-0-2#modernjs-framework) — While Rspack integrates with the Modern.js framework and covers many SSR capabilities, it does not yet provide full parity with Webpack’s SSR features. Additional testing is needed to assess its suitability for complex SSR applications.
- **Integration with Third-Party Tools** — Certain tools that integrate well with Webpack, such as [Storybook](https://rspack.dev/guide/migration/storybook), may not yet be fully compatible with Rspack, potentially requiring workarounds or custom configurations.
- **React 19 Compiler Requires Custom Configuration** — The [**React Compiler**](https://rspack.dev/guide/tech/react#react-compiler), introduced in React 19, currently only supports **Babel compilation**, which can slow down build performance. To use it with Rspack, developers must manually configure babel-loader alongside Rspack’s builtin:swc-loader, increasing complexity. This additional setup makes Rspack integration more involved compared to Webpack, especially for projects that require optimal build performance.

> Thanks to the close collaboration and parallel iteration of the [Modern.js framework](https://modernjs.dev/en/) and Rspack, Modern.js Rspack mode has covered 85% of the framework’s capabilities, supporting SSR, BFF, micro front-end scenarios, and aligning with TypeScript type checking, code compatibility detection and other features. — [https://rspack.dev/blog/announcing-0-2#modernjs-framework](https://rspack.dev/blog/announcing-0-2#modernjs-framework)

#### Use Case Analysis for ekino

Rspack effectively addresses two key **Client-Side Rendering (CSR)** scenarios at ekino:

**1️⃣ Migrating Legacy Webpack Projects (Including Module Federation)**

- **Ideal for projects reliant on CommonJS** and not **ESM-compatible**.
- Provides a **seamless migration path** when **Vite or Next.js** are not viable.
- Maintains **Webpack compatibility** while significantly improving **build performance**.
- **Supports Module Federation**, allowing **progressive migration** between Webpack and Rspack without full rewrites.

**2️⃣ New Micro-Frontend Projects with Module Federation (CSR Only)**

- **First-class support** for **Module Federation**, making it ideal for scalable micro-frontends.
- Enables **runtime dependency sharing** between **Webpack and Rspack applications**.
- Offers **faster builds** and lower overhead compared to Webpack’s federation.

🚩 **Limitations: **SSR support is not fully mature.

💡 **Recommendation:**

- **For legacy Webpack projects**, Rspack is the best migration path, **especially when Module Federation is used**.
- **For new CSR-based micro-frontends**, Rspack provides **robust Module Federation support** and **superior performance** over Webpack.

✳️ Based on our evaluations, we categorize build tools according to **project type and compatibility needs**:

✔️ **Vite for CSR (ESM-Compatible Projects)** — Best for **new ESM-based projects** with modern frameworks.

✔️ **Rspack for Webpack Migration (Non-ESM Projects)** — Ideal for **legacy Webpack projects needing a seamless transition**, especially with **Module Federation**.

✔️ **Next.js for SSR/SSG/ISR** — The go-to **framework for server-side rendering**, as Rspack’s SSR support is still evolving.

This structured approach ensures **optimal performance, maintainability, and scalability** based on project requirements.

### Conclusion

**Rspack presents a compelling alternative to Webpack, delivering faster builds, better resource efficiency, and seamless compatibility.**

Our evaluation confirms that Rspack excels in two key areas:

- **Migrating legacy Webpack projects** without breaking changes, particularly for applications that are not ESM-compatible.
- **Micro-frontend architectures with Module Federation**, where Rspack’s optimizations offer significant performance gains over Webpack.

However, **Rspack’s ecosystem is still evolving**, and **full SSR support remains a work in progress**, making it **less suited for SSR-heavy applications** at this stage.

Our proposal is to follow a structured approach that is tailored to the project requirements:

- **Vite** — Best for modern ESM-based CSR applications.
- **Rspack** — Ideal for migrating legacy Webpack projects and micro-frontends with Module Federation.
- **Next.js** — The preferred choice for SSR/SSG/ISR applications.

By aligning the right tool with each use case, we optimize **build performance, maintainability, and developer experience** across projects at ekino.

📌 **For more insights, check out our previous study:**
👉 [Beyond Webpack: esbuild, Vite, Rollup, SWC, and Snowpack](https://medium.com/ekino-france/beyond-webpack-esbuild-vite-rollup-swc-and-snowpack-97911a7175cf#60e8)

Until our paths meet again — **happy reading and coding! **♥️

```
Want to Connect? 
You can find me at ekino: https://github.com/helabenkhalfallah
```

---

[Rspack: An Engineer’s Approach to Build System Innovation](https://medium.com/ekino-france/rspack-an-engineers-approach-to-build-system-innovation-924e57c596a0) was originally published in [ekino-france](https://medium.com/ekino-france) on Medium, where people are continuing the conversation by highlighting and responding to this story.
