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
yarn
pnpm
bun
npm remove react-scripts

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

  • Install Rsbuild dependencies:
npm
yarn
pnpm
bun
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.

package.json
{
  "scripts": {
-   "start": "react-scripts start",
-   "build": "react-scripts build",
-   "eject": "react-scripts eject"
+   "start": "rsbuild dev",
+   "build": "rsbuild build",
+   "preview": "rsbuild preview"
  }
}
TIP

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. You can refer to the Rsbuild react-jest example project for configuration.

Creating Configuration File

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

rsbuild.config.ts
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:

rsbuild.config.ts
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:

rsbuild.config.ts
export default {
  output: {
    distPath: {
      root: 'build',
    },
  },
};

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

Using CSS Preprocessors

Rsbuild supports CSS preprocessors such as Sass and Less through plugins. Please refer to:

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 = () => (
  <div>
    <Logo />
  </div>
);

You only need to install and register @rsbuild/plugin-svgr:

rsbuild.config.ts
import { pluginSvgr } from '@rsbuild/plugin-svgr';

export default {
  plugins: [pluginSvgr({ mixedImport: true })],
};

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
HOST server.host
PORT server.port
HTTPS server.https
WDS_SOCKET_HOST dev.client.host
WDS_SOCKET_PATH dev.client.path
WDS_SOCKET_PORT dev.client.port
PUBLIC_URL dev.assetPrefix / output.assetPrefix
BUILD_PATH output.distPath
GENERATE_SOURCEMAP output.sourceMap
IMAGE_INLINE_SIZE_LIMIT output.dataUriLimit
FAST_REFRESH dev.hmr
TSC_COMPILE_ON_ERROR @rsbuild/plugin-type-check

Notes:

  • 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.

Env 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.

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

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

export default defineConfig({
  source: {
    define: publicVars,
  },
});

Note that CRA allows access to the full process.env object in the code and also allows destructuring of process.env. However, Rsbuild does not define the process.env object due to bundle size and security concerns.

src/index.js
// In CRA, you can access it like this
const { PUBLIC_URL } = process.env;
console.log(PUBLIC_URL);
console.log(process.env);

In Rsbuild, you can use the source.define config to set process.env and read the rawPublicVars returned by the loadEnv method to allow the above usage:

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

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

export default defineConfig({
  source: {
    define: {
      ...publicVars,
      'process.env': JSON.stringify(rawPublicVars),
    },
  },
});

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:

index.js
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:

rsbuild.config.ts
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.

src/index.js
- import 'react-app-polyfill/ie11';
- import 'react-app-polyfill/stable';

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

Add ESLint plugin

CRA has the eslint-webpack-plugin enabled by default and includes a set of built-in ESLint rules.

In an Rsbuild project, if you need similar functionality, you can add @rsbuild/plugin-eslint and use eslint-config-react-app as the ESLint ruleset.

.eslintrc.cjs
module.exports = {
  extends: 'react-app',
};

Reading jsconfig.json

In non-TypeScript projects, CRA supports reading the paths field in jsconfig.json as the path alias.

If you want to use this feature in Rsbuild, you can set the source.tsconfigPath option, so that Rsbuild can recognize the paths field in jsconfig.json.

rsbuild.config.mjs
export default {
  source: {
    tsconfigPath: './jsconfig.json',
  },
};

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.add appendPlugins of tools.rspack
webpack.plugins.remove removePlugin of tools.rspack
style.modules output.cssModules
style.css tools.cssLoader
style.sass Sass Plugin
style.postcss tools.postcss
babel Babel Plugin
typescript @rsbuild/plugin-type-check
devServer server configs

Example

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

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

module.exports = {
  webpack: {
    configure: {
      resolve: {
        mainFields: ['browser', 'module', 'main'],
      },
    },
    plugins: [...whenDev(() => [new MyWebpackPlugin()], [])],
  },
};
  • After migration:
rsbuild.config.ts
export default {
  tools: {
    rspack: {
      resolve: {
        mainFields: ['browser', 'module', 'main'],
      },
      plugins:
        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/website directory.