Build React Apps 10x Faster with Rspack and Rsbuild: The Rust-Powered Bundler Guide

AI Bot
By AI Bot ·

Loading the Text to Speech Audio Player...

If you have ever waited minutes for your webpack build to finish, you know the pain. Rspack and Rsbuild are here to change that. Written in Rust, Rspack is a drop-in webpack replacement that delivers build speeds up to 10x faster, while Rsbuild is the higher-level build tool that provides an out-of-the-box development experience on top of it.

In this tutorial, you will build a complete React application from scratch using Rsbuild, then learn how to configure Rspack directly for advanced use cases. You will also learn how to migrate an existing webpack project step by step.

Prerequisites

Before starting, make sure you have:

  • Node.js 18+ installed (Node 20 recommended)
  • npm, yarn, or pnpm as your package manager
  • Basic knowledge of React and TypeScript
  • A code editor (VS Code recommended)
  • Optional: An existing webpack project if you plan to migrate

What You Will Build

By the end of this tutorial, you will have:

  1. A new React + TypeScript app built with Rsbuild from scratch
  2. Understanding of Rspack configuration for advanced control
  3. Knowledge to migrate any webpack project to Rspack

Understanding the Rspack Ecosystem

Before diving into code, let us understand the two main tools:

  • Rspack is the low-level bundler, a Rust-based replacement for webpack. It is compatible with most webpack plugins and loaders while being dramatically faster.
  • Rsbuild is the high-level build tool built on top of Rspack. Think of it as what Create React App or Vite is for their respective bundlers, but powered by Rspack.

When to use which?

Use CaseTool
New React projectRsbuild
Need full webpack-like controlRspack CLI
Migrating from CRARsbuild
Migrating from webpackRspack (then optionally Rsbuild)
Library bundlingRspack

Step 1: Create a New React Project with Rsbuild

The fastest way to get started is using Rsbuild's project generator:

npm create rsbuild@latest

When prompted, select:

  • Project name: my-rsbuild-app
  • Framework: React
  • Language: TypeScript

This creates a project with sensible defaults. Navigate into it and install dependencies:

cd my-rsbuild-app
npm install

Start the development server:

npm run dev

Your app should be running at http://localhost:3000 with blazing-fast Hot Module Replacement (HMR).

Step 2: Explore the Project Structure

The generated project has a clean structure:

my-rsbuild-app/
  src/
    App.tsx
    App.css
    index.tsx
    env.d.ts
  rsbuild.config.ts
  tsconfig.json
  package.json

The key file is rsbuild.config.ts. Open it:

import { defineConfig } from '@rsbuild/core';
import { pluginReact } from '@rsbuild/plugin-react';
 
export default defineConfig({
  plugins: [pluginReact()],
});

That is it. Two lines of configuration and you have a fully working React + TypeScript setup with:

  • SWC-based compilation (no Babel needed)
  • CSS Modules support
  • Fast Refresh (HMR)
  • Automatic code splitting
  • Environment variable loading

Step 3: Configure Rsbuild for Production

Let us add production-ready configuration. Update rsbuild.config.ts:

import { defineConfig } from '@rsbuild/core';
import { pluginReact } from '@rsbuild/plugin-react';
 
export default defineConfig({
  plugins: [
    pluginReact({
      swcReactOptions: {
        runtime: 'automatic',
      },
      splitChunks: {
        react: true,
        router: true,
      },
    }),
  ],
  source: {
    entry: {
      index: './src/index.tsx',
    },
  },
  output: {
    distPath: {
      root: 'dist',
    },
    cleanDistPath: true,
    sourceMap: {
      js: process.env.NODE_ENV === 'production'
        ? 'source-map'
        : 'cheap-module-source-map',
    },
  },
  html: {
    title: 'My Rsbuild App',
    favicon: './src/favicon.ico',
  },
  server: {
    port: 3000,
    open: true,
  },
  performance: {
    chunkSplit: {
      strategy: 'split-by-experience',
    },
  },
});

What each section does:

  • plugins: Registers the React plugin with chunk splitting for React and React Router
  • source.entry: Defines the application entry point
  • output: Configures build output, source maps, and cleaning
  • html: Sets the page title and favicon
  • server: Dev server configuration with auto-open
  • performance.chunkSplit: Splits vendor chunks intelligently based on common patterns

Step 4: Add CSS Modules and Styling

Rsbuild supports CSS Modules out of the box. Create a component with scoped styles:

/* src/components/Card.module.css */
.card {
  border: 1px solid #e0e0e0;
  border-radius: 12px;
  padding: 24px;
  transition: box-shadow 0.2s ease;
}
 
.card:hover {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
 
.title {
  font-size: 1.25rem;
  font-weight: 600;
  margin-bottom: 8px;
}
 
.description {
  color: #666;
  line-height: 1.6;
}
// src/components/Card.tsx
import styles from './Card.module.css';
 
interface CardProps {
  title: string;
  description: string;
}
 
export function Card({ title, description }: CardProps) {
  return (
    <div className={styles.card}>
      <h3 className={styles.title}>{title}</h3>
      <p className={styles.description}>{description}</p>
    </div>
  );
}

Any file ending in .module.css is automatically treated as a CSS Module. No configuration needed.

Step 5: Environment Variables

Rsbuild loads .env files automatically. Create a .env file:

# .env
PUBLIC_API_URL=https://api.example.com
PUBLIC_APP_NAME=My Rsbuild App

Access them in your code:

// Variables prefixed with PUBLIC_ are available in client code
console.log(process.env.PUBLIC_API_URL);
console.log(process.env.PUBLIC_APP_NAME);

You can customize the prefix in your config:

export default defineConfig({
  loadEnv: {
    prefixes: ['PUBLIC_', 'REACT_APP_'],
  },
});

This is especially useful when migrating from Create React App, where variables use the REACT_APP_ prefix.

Step 6: Advanced Rspack Configuration

For projects that need webpack-level control, you can use Rspack directly. Here is a complete rspack.config.ts for a React + TypeScript project:

import { defineConfig } from '@rspack/cli';
import { rspack, type SwcLoaderOptions } from '@rspack/core';
import { ReactRefreshRspackPlugin } from '@rspack/plugin-react-refresh';
 
const isDev = process.env.NODE_ENV === 'development';
const targets = ['last 2 versions', '> 0.2%', 'not dead'];
 
export default defineConfig({
  entry: {
    main: './src/index.tsx',
  },
  output: {
    path: './dist',
    filename: isDev ? '[name].js' : '[name].[contenthash:8].js',
    cssFilename: isDev ? '[name].css' : '[name].[contenthash:8].css',
    clean: true,
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.jsx', '.js'],
    alias: {
      '@': './src',
    },
  },
  module: {
    rules: [
      {
        test: /\.(tsx?|jsx?)$/,
        exclude: /node_modules/,
        use: {
          loader: 'builtin:swc-loader',
          options: {
            jsc: {
              parser: {
                syntax: 'typescript',
                tsx: true,
              },
              transform: {
                react: {
                  runtime: 'automatic',
                  development: isDev,
                  refresh: isDev,
                },
              },
            },
            env: { targets },
          } satisfies SwcLoaderOptions,
        },
      },
      {
        test: /\.css$/,
        type: 'css/auto',
      },
      {
        test: /\.(png|jpe?g|gif|svg|webp)$/,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 8 * 1024, // 8KB
          },
        },
      },
    ],
  },
  plugins: [
    new rspack.HtmlRspackPlugin({
      template: './index.html',
    }),
    new rspack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
    }),
    isDev && new ReactRefreshRspackPlugin(),
  ].filter(Boolean),
  devServer: {
    port: 3000,
    hot: true,
    historyApiFallback: true,
  },
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendor',
          chunks: 'all',
        },
      },
    },
  },
});

Key differences from webpack:

  1. builtin:swc-loader replaces babel-loader entirely. It is written in Rust and compiles TypeScript and JSX natively.
  2. css/auto type handles CSS files including CSS Modules detection automatically.
  3. rspack.HtmlRspackPlugin is a Rust-native replacement for html-webpack-plugin.
  4. ReactRefreshRspackPlugin replaces @pmmmwh/react-refresh-webpack-plugin.

Step 7: Migrate an Existing Webpack Project

If you have an existing webpack project, follow these steps:

7.1: Install Rspack

npm install @rspack/core @rspack/cli -D
npm install @rspack/plugin-react-refresh -D

7.2: Rename Configuration

mv webpack.config.js rspack.config.js

7.3: Update Package Scripts

{
  "scripts": {
    "dev": "rspack serve",
    "build": "rspack build"
  }
}

7.4: Replace babel-loader with builtin:swc-loader

Remove Babel dependencies:

npm uninstall babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript

Replace the babel-loader rule in your config:

// Before (webpack)
{
  test: /\.(js|jsx|ts|tsx)$/,
  exclude: /node_modules/,
  use: {
    loader: 'babel-loader',
    options: {
      presets: [
        '@babel/preset-env',
        '@babel/preset-react',
        '@babel/preset-typescript',
      ],
    },
  },
}
 
// After (rspack)
{
  test: /\.(js|jsx|ts|tsx)$/,
  exclude: /node_modules/,
  use: {
    loader: 'builtin:swc-loader',
    options: {
      jsc: {
        parser: {
          syntax: 'typescript',
          tsx: true,
        },
        transform: {
          react: {
            runtime: 'automatic',
            development: process.env.NODE_ENV === 'development',
            refresh: process.env.NODE_ENV === 'development',
          },
        },
      },
    },
  },
}

7.5: Replace Plugins

webpack PluginRspack Replacement
html-webpack-pluginrspack.HtmlRspackPlugin
mini-css-extract-pluginBuilt-in (use type: 'css/auto')
copy-webpack-pluginrspack.CopyRspackPlugin
DefinePluginrspack.DefinePlugin
react-refresh-webpack-plugin@rspack/plugin-react-refresh

7.6: Handle CSS

Replace css-loader + style-loader chain with the built-in CSS type:

// Before (webpack)
{
  test: /\.css$/,
  use: ['style-loader', 'css-loader'],
}
 
// After (rspack)
{
  test: /\.css$/,
  type: 'css/auto',
}

Sass/SCSS Support

npm install @rsbuild/plugin-sass -D
import { pluginSass } from '@rsbuild/plugin-sass';
 
export default defineConfig({
  plugins: [
    pluginReact(),
    pluginSass(),
  ],
});

SVG as React Components

npm install @rsbuild/plugin-svgr -D
import { pluginSvgr } from '@rsbuild/plugin-svgr';
 
export default defineConfig({
  plugins: [
    pluginReact(),
    pluginSvgr({
      svgrOptions: {
        exportType: 'named',
      },
    }),
  ],
});

Use SVGs as components:

import { ReactComponent as Logo } from './logo.svg';
 
function App() {
  return <Logo width={100} height={100} />;
}

Tailwind CSS

No special plugin needed. Just install Tailwind and configure it normally:

npm install tailwindcss @tailwindcss/postcss postcss -D

Create postcss.config.js:

export default {
  plugins: {
    '@tailwindcss/postcss': {},
  },
};

Then import Tailwind in your CSS:

@import "tailwindcss";

Rsbuild picks up PostCSS configuration automatically.

Step 9: Build and Analyze

Run the production build:

npm run build

You will notice the build completes significantly faster than webpack. For a medium-sized React project, expect:

MetricwebpackRspack
Cold build30-60s3-6s
HMR update500-2000ms50-200ms
Dev server start10-20s1-3s

To analyze your bundle:

RSBUILD_BUNDLE_ANALYZE=true npm run build

This generates an interactive bundle analysis report in your browser.

Testing Your Implementation

Verify everything works correctly:

  1. Dev server: Run npm run dev and confirm HMR works by editing a component
  2. CSS Modules: Check that styles are scoped (class names should be hashed)
  3. TypeScript: Introduce a type error and verify it shows in the terminal
  4. Production build: Run npm run build and serve the dist folder
  5. Environment variables: Add a PUBLIC_ variable and log it in a component

Troubleshooting

"Module not found" for webpack loaders

If you see errors about missing loaders, you are likely using a webpack-specific loader. Check if Rspack has a built-in replacement (most common ones do).

CSS not being extracted in production

Make sure you are using type: 'css/auto' in your Rspack rules. Rsbuild handles this automatically.

React Fast Refresh not working

Ensure ReactRefreshRspackPlugin is only added in development mode and that refresh: true is set in your SWC options.

Webpack plugins not compatible

While most webpack plugins work, some that rely on internal webpack APIs may not. Check the Rspack compatibility list at rspack.dev for alternatives.

Next Steps

  • Explore the full Rsbuild plugin ecosystem at rsbuild.dev
  • Set up Storybook with Rsbuild using storybook-rsbuild
  • Try Module Federation for micro-frontends with Rspack
  • Add testing with Vitest (works seamlessly alongside Rsbuild)
  • Check out Rslib for building library packages with Rspack

Conclusion

Rspack and Rsbuild bring the speed of Rust to the JavaScript bundling world without sacrificing webpack compatibility. Whether you are starting a new project with Rsbuild's zero-config approach or migrating an existing webpack project to Rspack, you get dramatic build speed improvements with minimal effort.

The key takeaway: start with Rsbuild for new projects (it handles the complexity for you), and use Rspack directly when you need fine-grained webpack-level control. Either way, your builds will be faster, your development experience smoother, and your team happier.


Want to read more tutorials? Check out our latest tutorial on Build a Production Monorepo with Turborepo, Next.js, and Shared Packages.

Discuss Your Project with Us

We're here to help with your web development needs. Schedule a call to discuss your project and how we can assist you.

Let's find the best solutions for your needs.

Related Articles

Build a Real-Time Full-Stack App with Convex and Next.js 15

Learn how to build a real-time full-stack application using Convex and Next.js 15. This tutorial covers schema design, queries, mutations, real-time subscriptions, authentication, and file uploads — all with end-to-end type safety.

30 min read·

Building Local-First Collaborative Apps with Yjs and React

Learn how to build real-time collaborative applications that work offline using Yjs CRDTs and React. This tutorial covers conflict-free data synchronization, offline-first architecture, and building a shared document editor from scratch.

30 min read·