Rspack vs Vite sounds settled if you only read launch-day benchmarks: everything is "10x faster" than Webpack, and the charts use a 1,000-module demo with nothing real in it. A 500+ component React app that has been running on Webpack 5 for four years is a different problem. It carries require.context calls, a Babel plugin nobody remembers adding, three CSS strategies and a process.env in every other file. This post compares Rspack 1.5 and Vite 6 on that kind of webpack migration: HMR latency, cold start, memory, and the plugin gaps that only show up once you deploy.
If you want the greenfield view, our Vite vs Turbopack vs Rspack bundler comparison covers new projects. This one is about moving an existing app.
The test app and how we measured
The reference app is a React 18 + TypeScript SPA with 540 components, about 2,900 modules after resolution, Sass modules plus styled-components, and a Webpack 5 config built on babel-loader. Each figure is the median of 10 runs on a 16 GB M2 laptop with Node 22 and warm OS caches.
- Cold start: time from running the dev command to the first fully interactive render of the dashboard route, not just to "server ready".
- HMR latency: time from saving a leaf component to the DOM updating, measured with a
MutationObservertimestamp minus the file mtime. - Memory: peak RSS of the dev process after navigating 20 routes.
# Production build timing, 10 runs each
hyperfine --runs 10 --prepare 'rm -rf dist' \
'npx webpack --mode production' \
'npx rspack build' \
'npx vite build'What do the benchmark numbers actually show?
Rspack beats Webpack by roughly 7x on cold start and production builds, and keeps HMR under 200ms. Vite 6's server is ready almost instantly, but the first full page load on a big app is slower than Rspack's.
| Metric | Webpack 5 | Rspack 1.5 | Vite 6 |
|---|---|---|---|
| Cold start to interactive | 48s | 6.8s | 14s (server ready in 1.2s) |
| HMR, leaf component | 2.1s | 180ms | 90ms |
| HMR, shared context provider | 3.4s | 310ms | 1.6s |
| Peak dev RSS | 3.4 GB | 1.6 GB | 1.1 GB (plus about 900 MB in the browser tab) |
| Production build | 142s | 21s | 58s |
Two rows matter most. First, Vite wins leaf-component HMR and loses badly when you edit a module that half the app imports, because the browser then has to re-request and re-evaluate a large chunk of the graph over native ESM. Second, Vite's memory figure is flattering. The dev server is lean, but the browser tab holding 2,900 unbundled modules is not.
Why does Vite's cold start look fast but feel slow on big apps?
Vite's dev server serves source files unbundled over native ESM, so the browser has to fetch every module as a separate request. With 2,900 modules, that means thousands of HTTP requests before the dashboard renders, however fast the server says it is ready.
Dependency pre-bundling with esbuild handles node_modules, but your own source still arrives as a request waterfall. Deep barrel files make it worse: one import { Button } from '@/components' pulls in 200 modules. It took us about 20 minutes in the Network panel to work out why a route showed a spinner for 11 seconds, and the Vite docs mention barrel files only briefly under performance.
The fix is to delete barrels and use direct imports, but in a 540-component app that is a codemod plus a week of review. Rspack bundles in dev, so barrels hurt build time but don't flood the network.
Rspack 1.5: the pragmatic Webpack migration
Verdict: Rspack 1.5 is the right call for any large React app currently on Webpack. The config API is Webpack's, most loaders work unchanged, and we had a working dev server on day one.
The migration is mostly deletions. Replace babel-loader with the built-in SWC loader, swap a couple of plugins for their built-in versions, and turn on the persistent cache:
// rspack.config.ts
import { defineConfig } from '@rspack/cli';
import { rspack } from '@rspack/core';
import ReactRefreshPlugin from '@rspack/plugin-react-refresh';
const isDev = process.env.NODE_ENV === 'development';
export default defineConfig({
entry: { main: './src/index.tsx' },
resolve: { extensions: ['.ts', '.tsx', '.js'] },
experiments: { cache: { type: 'persistent' } },
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'builtin:swc-loader',
options: {
jsc: {
parser: { syntax: 'typescript', tsx: true },
transform: { react: { runtime: 'automatic', development: isDev, refresh: isDev } },
},
},
},
{ test: /\.module\.scss$/, use: ['sass-loader'], type: 'css/module' },
],
},
plugins: [
new rspack.HtmlRspackPlugin({ template: './public/index.html' }),
isDev && new ReactRefreshPlugin(),
].filter(Boolean),
});Where it still hurts:
- Babel plugins:
babel-plugin-styled-componentsbecomes@swc/plugin-styled-components, and SWC Wasm plugins are pinned to specificswc_coreversions. Get it wrong and you see a cryptic panic at startup. Check the Rspack compatibility table before you upgrade anything. - Type checking:
fork-ts-checker-webpack-pluginmostly works, butts-checker-rspack-pluginis the supported route. Use it. - Long-tail plugins: anything that reaches into
compilationinternals, such as older Workbox setups or custom asset-manifest plugins, may silently do nothing. Diff yourdist/output against Webpack's before you ship.
Vite 6: faster DX, more expensive migration
Verdict: Vite 6 is the better long-term DX, but moving a large Webpack app to it is a rewrite, not a port. Budget weeks, not days.
The config is clean. Everything else is the work:
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc';
import svgr from 'vite-plugin-svgr';
export default defineConfig({
plugins: [react({ plugins: [['@swc/plugin-styled-components', {}]] }), svgr()],
define: { 'process.env.API_URL': JSON.stringify(process.env.API_URL) },
resolve: { alias: { '@': '/src' } },
build: { sourcemap: true },
});The gaps we hit:
require.contexthas to be rewritten asimport.meta.globby hand at every call site.- CommonJS-only internal packages break in dev until you add them to
optimizeDeps.include, and you only find out when a route throwsrequire is not defined. - Dev uses esbuild and native ESM while production uses Rollup, so a CSS ordering bug can exist only in production. That is the bundler version of "it works on my machine".
- Module Federation works through
@module-federation/vite, but it is less mature than Rspack's built-in MF 2.0 support.
The 58s production build is Rollup's ceiling. Rolldown-powered Vite closes most of that gap, but as of Vite 6 it is a separate package (rolldown-vite), not the default. For more Vite 6 numbers, see our Vite 6 vs Turbopack benchmarks.
Staying on Webpack 5
Verdict: staying on Webpack is only defensible if your release is frozen. A 48s cold start costs every developer several minutes a day, and slow CI builds cost money on every commit.
If you can't migrate this quarter, swap babel-loader for swc-loader and enable cache: { type: 'filesystem' }. That typically halves the pain and makes a later move to Rspack trivial, because you've already dropped Babel.
Which plugin gaps still bite in production?
Source maps and error monitoring. Both bundlers produce valid maps, but chunk names and debug IDs change after a migration, so your error tracker may stop matching stack traces without telling you.
Before cutting over, run a deliberate production error and confirm it de-minifies in Sentry. Its Rspack and Vite plugins upload maps with debug IDs, which survives hashed filenames. Then compare real-user performance before and after with DebugBear. A different chunking strategy can quietly regress LCP even when the bundle shrinks.
Verdict: Rspack vs Vite for a 500+ component React app
Migrate to Rspack 1.5. For an existing large Webpack codebase it gives you about 85% of the speed-up for about 10% of the migration effort, and dev and production use the same bundler. Pick Vite 6 only if you're also willing to remove barrel files, eliminate CommonJS and rewrite require.context, or if the app is small enough that the unbundled waterfall never shows up. A sensible plan is to move to Rspack now and reassess Vite once Rolldown is the default.
FAQs
Is Rspack 1.5 a drop-in replacement for Webpack 5?
Close, but not entirely. Most loaders and common plugins work unchanged, and the config shape is identical. Plugins that rely on Webpack compilation internals and Babel-only transforms are where you'll spend your migration time.
Is Vite 6 faster than Rspack for HMR in large React apps?
For leaf components, yes, slightly. For edits to widely imported modules such as context providers or theme files, Rspack is usually faster, because Vite has to re-fetch a large part of the module graph over native ESM.
How long does a Webpack to Rspack migration take?
For a 500-component React app, expect one to three days to get a working build, then about a week of verifying output parity, source maps and edge-case plugins. Vite migrations of the same size typically take several weeks.
Does Rspack support Module Federation?
Yes. Rspack supports Module Federation natively, including MF 2.0 via @module-federation/enhanced. That makes it the safer choice for micro-frontend setups migrating off Webpack.