Create React App

This chapter introduces how to migrate a Create React App (CRA) or CRACO project to Rsbuild.

CRA eject

If your project has already run the CRA eject command, then most of the content in this document will no longer be applicable.

After ejecting a CRA project, it becomes more like a project directly using webpack, so you can refer to the webpack migration guide.

Installing Dependencies

First, you need to replace the npm dependencies of CRA with Rsbuild's dependencies.

  • Remove CRA dependencies:
npm remove react-scripts

For projects using CRACO, you can also remove the @craco/craco dependency.

  • Install Rsbuild dependencies:
npm add @rsbuild/core @rsbuild/plugin-react -D

Updating npm scripts

Next, you need to update the npm scripts in package.json to Rsbuild's CLI commands.

  "scripts": {
-   "start": "react-scripts start",
-   "build": "react-scripts build",
-   "eject": "react-scripts eject"
+   "start": "rsbuild dev",
+   "build": "rsbuild build",
+   "preview": "rsbuild preview"

Rsbuild does not integrate testing frameworks, so it does not provide a command to replace react-scripts test. You can directly use testing frameworks such as Jest or Vitest.

Creating Configuration File

Create a Rsbuild configuration file rsbuild.config.ts in the same directory as package.json and add the following content:

import { defineConfig } from '@rsbuild/core';
import { pluginReact } from '@rsbuild/plugin-react';

export default defineConfig({
  plugins: [pluginReact()],

HTML Template

CRA uses the public/index.html file as the default HTML template. In Rsbuild, you can specify the HTML template through html.template:

export default defineConfig({
  html: {
    template: './public/index.html',

In the HTML template, if you are using the %PUBLIC_URL% variable from CRA, replace it with Rsbuild's assetPrefix variable and use a forward slash for concatenation:

-  <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
+  <link rel="icon" href="<%= assetPrefix %>/favicon.ico" />

This completes the basic migration from CRA to Rsbuild. You can now run the npm run start command to try starting the dev server.

Output Directory

By default, CRA outputs to the build directory, while Rsbuild's default output directory is dist.

You can configure Rsbuild's output.distPath.root option to change the directory to build, in line with CRA:

export default {
  output: {
    distPath: {
      root: 'build',

For more details, please refer to the Output Files section.

Using SVGR

If you are using the "SVG to React Component" feature of CRA (i.e., SVGR), you also need to install the SVGR plugin for Rsbuild.

For example, if you are using the following usage:

import { ReactComponent as Logo } from './logo.svg';

const App = () => (
    <Logo />

Please refer to the SVGR plugin documentation to learn how to use SVGR in Rsbuild.

Configuration Migration

Here is the corresponding Rsbuild configuration for CRA configuration:

CRA Rsbuild
PORT server.port
HTTPS server.https
WDS_SOCKET_PATH dev.client.path
WDS_SOCKET_PORT dev.client.port
PUBLIC_URL dev.assetPrefix / output.assetPrefix
BUILD_PATH output.distPath


  • The above table does not cover all configurations of CRA, feel free to add more.

Compile node_modules

By default, CRA uses Babel to compile dependencies in node_modules, but Rsbuild does not, to avoid the performance overhead and potential compilation errors caused by secondary compilation.

If you need to handle syntax compatibility issues caused by dependencies in node_modules, you can use the source.include config to compile node_modules.

Environment Variables

CRA injects environment variables starting with REACT_APP_ into the client code by default, while Rsbuild injects environment variables starting with PUBLIC_ by default (see public variables).

To be compatible with CRA's behavior, you can manually call Rsbuild's loadEnv method to read environment variables starting with REACT_APP_, and inject them into the client code through the source.define config.

import { defineConfig, loadEnv } from '@rsbuild/core';

const { publicVars } = loadEnv({ prefixes: ['REACT_APP_'] });

export default defineConfig({
  source: {
    define: publicVars,

Import Unknown Assets

In CRA, if you import an asset that the build tool cannot recognize, CRA will by default output the file to the build/static/media directory, for example, the document.pdf file:

import document from './document.pdf';

In Rsbuild, when importing unrecognized assets, Rsbuild will logs errors:

You may need an appropriate loader to handle this file type.

To resolve this error, you can use the following methods:

For example, you can add the following asset modules config to get the same output result as CRA:

export default {
  tools: {
    rspack: {
      module: {
        rules: [
            // Match .png asset
            // You can change this regular expression to match different types of files
            test: /\.png$/,
            type: 'asset/resource',
            generator: {
              filename: 'static/media/[name].[hash][ext]',

Remove react-app-polyfill

CRA provides react-app-polyfill to manually inject polyfill code.

In the Rsbuild project, you can remove the dependency and code related to react-app-polyfill, as Rsbuild will automatically read the browserslist config and inject polyfill code as needed.

- import 'react-app-polyfill/ie11';
- import 'react-app-polyfill/stable';

You can read Browser Compatibility to understand how Rsbuild handles polyfills.

CRACO Migration

If your project is using CRACO to override CRA configuration, you can refer to the table below for migration:

CRACO Rsbuild
webpack.configure tools.rspack
webpack.alias source.alias
webpack.plugins tools.rspack.appendPlugins
webpack.plugins.remove tools.rspack.removePlugin
style.modules output.cssModules
style.css tools.cssLoader
style.sass tools.sass
style.postcss tools.postcss
babel Babel Plugin
typescript Type Check Plugin
devServer server configs


Here is an example of migrating from webpack.configure to tools.rspack:

  • Before migration:
const { whenDev } = require('@craco/craco');

module.exports = {
  webpack: {
    configure: {
      resolve: {
        mainFields: ['browser', 'module', 'main'],
    plugins: [...whenDev(() => [new MyWebpackPlugin()], [])],
  • After migration:
export default {
  tools: {
    rspack: {
      resolve: {
        mainFields: ['browser', 'module', 'main'],
        process.env.NODE_ENV === 'development' ? [new MyWebpackPlugin()] : [],

Contents Supplement

The current document only covers part of the migration process. If you find suitable content to add, feel free to contribute to the documentation via pull request 🤝.

The documentation for rsbuild can be found in the rsbuild/packages/document directory.