Migrating Excalidraw from Webpack to Rspack: A Step‑by‑Step Guide
This article demonstrates how to migrate the open‑source Excalidraw drawing application from Webpack to the Rust‑based Rspack bundler, covering repository cloning, dependency installation, Rspack initialization, Sass, HTML plugin, environment variable handling, static asset copying, performance comparison, and the use of react‑scripts‑rspack for automated migration.
Last month ByteDance open‑sourced Rspack, a new Rust‑based build engine that maintains good compatibility with the Webpack API while delivering a 5‑10× performance boost.
In this tutorial we use the popular open‑source drawing tool Excalidraw (written in TypeScript and built with create‑react‑app ) as a real‑world example to test Rspack.
Clone the Excalidraw repository
git clone [email protected]:excalidraw/excalidraw.gitInstall dependencies and start the project:
yarn
yarn startThe application launches successfully, confirming the original setup works.
Initialize Rspack
Install the Rspack CLI:
yarn add @rspack/cliConfigure the entry point in package.json and create rspack.config.js :
{
"scripts": {
"build:rspack": "rspack build",
"start:rspack": "rspack serve"
},
"dependencies": {
"@rspack/cli": "0.1.4"
}
} module.exports = {
context: __dirname,
entry: { main: './src/index.tsx' }
};Sass compilation
Add sass-loader to package.json and configure it in rspack.config.js :
{
"dependencies": { "sass-loader": "13.2.2" }
} module.exports = {
module: {
rules: [
{
test: /\.scss$/,
use: [{ loader: "sass-loader" }],
type: 'css'
}
]
}
};HTML plugin
Install @rspack/plugin-html and configure it to process the HTML template:
{
"dependencies": { "@rspack/plugin-html": "0.1.4" }
} module.exports = {
plugins: [
new html({
template: "./public/index.html",
templateParameters: false
})
]
};Environment variables
Install dotenv and expand it to load .env.development or .env.production files, then define the variables for Rspack:
{
"dependencies": { "dotenv": "16.0.1" }
} const env = process.env.NODE_ENV || "development";
const dotEnvFiles = env === "development" ? [".env.development"] : [".env.production"];
dotEnvFiles.forEach(doteEnvFile => {
require("dotenv-expand")(require("dotenv").config({ path: doteEnvFile }));
});
const REACT_APP = /^REACT_APP_/i;
const filterEnv = {};
const define = Object.keys(process.env)
.filter(key => REACT_APP.test(key))
.reduce((env, key) => {
filterEnv[key] = process.env[key];
env[`process.env.${key}`] = JSON.stringify(process.env[key]);
return env;
}, {});
module.exports = {
builtins: {
define: {
...define,
"import.meta.env && import.meta.env.MODE": JSON.stringify(env),
"process.env": JSON.stringify(filterEnv)
}
}
};Static assets (favicon)
Use Rspack’s built‑in copy feature (equivalent to copy‑webpack‑plugin ) to copy the public folder except index.html :
module.exports = {
builtins: {
copy: {
patterns: [{
from: "public",
globOptions: { ignore: ["**/index.html"] }
}]
}
}
};Performance comparison
After migration the build time dropped from 51 s to 3 s , a more than ten‑fold improvement.
Full Rspack configuration
const html = require("@rspack/plugin-html").default;
const env = process.env.NODE_ENV || "development";
const dotEnvFiles = env === "development" ? [".env.development"] : [".env.production"];
dotEnvFiles.forEach(doteEnvFile => {
require("dotenv-expand")(require("dotenv").config({ path: doteEnvFile }));
});
const REACT_APP = /^REACT_APP_/i;
const filterEnv = {};
const define = Object.keys(process.env)
.filter(key => REACT_APP.test(key))
.reduce((env, key) => {
filterEnv[key] = process.env[key];
env[`process.env.${key}`] = JSON.stringify(process.env[key]);
return env;
}, {});
module.exports = {
entry: { main: "./src/index.tsx" },
module: {
rules: [{
test: /\.scss$/,
use: [{ loader: "sass-loader" }],
type: "css"
}]
},
builtins: {
define: {
...define,
"import.meta.env && import.meta.env.MODE": JSON.stringify(process.env.NODE_ENV || "production"),
"process.env": JSON.stringify(filterEnv)
},
copy: {
patterns: [{
from: "public",
globOptions: { ignore: ["**/index.html"] }
}]
}
},
plugins: [
new html({
template: "./public/index.html",
templateParameters: false
})
]
};One‑click migration with react-scripts-rspack
Install the helper package and run the familiar commands:
// Development
react-scripts-rspack start
// Production build
react-scripts-rspack buildSee the real migration PR for reference: https://github.com/excalidraw/excalidraw/pull/6425
Conclusion
The migration of a relatively complex Webpack project like Excalidraw to Rspack is straightforward thanks to Rspack’s API compatibility and its support for common loaders and plugins such as sass-loader , HTML and copy plugins. The effort yields a ten‑fold build‑time reduction, making Rspack an attractive option for future projects.
ByteDance Web Infra
ByteDance Web Infra team, focused on delivering excellent technical solutions, building an open tech ecosystem, and advancing front-end technology within the company and the industry | The best way to predict the future is to create it
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.