rspack-plugin-react-refresh

Adds React Fast Refresh support to Rspack builds through runtime injection and source instrumentation.

Library
npm
v2.0.2
20stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
61/100Good
Development Activity80
Maintenance84
Community24
Maturity44
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
74/100Good
Architecture78
Code Quality82
Innovation80
Learning Curve55

@rspack/plugin-react-refresh integrates React Fast Refresh into Rspack, the Rust-based webpack-compatible bundler used by tools like Rspress, Modern.js, and rsbuild. It handles the runtime side of Fast Refresh — injecting the react-refresh runtime and a $ReactRefreshRuntime$ global via Rspack’s ProvidePlugin and DefinePlugin — while delegating source transformation to a loader such as the built-in SWC loader’s jsc.transform.react.refresh option or babel-loader. This separation lets teams swap the code-transform layer freely without touching how the refresh runtime is wired into the compilation.

The plugin only activates in development mode by default (or when forceEnable is set, for use with mode: 'none' or unsupported environments like electron-prerender), and can be scoped to specific modules via test, include, exclude, and resourceQuery conditions that mirror Rspack’s own module-rule matching. A library option namespaces the refresh runtime for micro-frontend setups running multiple independent instances side by side, and reloadOnRuntimeErrors forces a full page reload when Rspack hits an unrecoverable runtime error such as an undefined module factory.

What You Get

  • Automatic react-refresh runtime injection via Rspack’s ProvidePlugin, with no manual wiring of $ReactRefreshRuntime$ required
  • A configurable builtin:react-refresh-loader instrumentation step that composes with builtin:swc-loader, swc-loader, or babel-loader
  • Fine-grained test/include/exclude/resourceQuery matching so refresh instrumentation can be scoped file-by-file, mirroring Rspack’s own rule syntax
  • A library namespace option for running multiple independent React Refresh instances in micro-frontend architectures
  • reloadOnRuntimeErrors to force a full-page reload on unrecoverable runtime errors like an undefined module factory

Common Use Cases

  • Enabling hot component reloading in an Rspack-based dev server for a React app without losing component state on save
  • Migrating a webpack + react-refresh-webpack-plugin setup to Rspack while keeping the same fast-refresh developer experience
  • Running multiple React apps or micro-frontends on the same page, each with an isolated refresh runtime via the library option
  • Excluding vendor bundles or asset-loaded modules (e.g. new URL(..., import.meta.url)) from refresh instrumentation to avoid unnecessary rebuilds

Under The Hood

Architecture The plugin is a single ReactRefreshRspackPlugin class (src/index.ts) with a plain apply(compiler) method — the standard Rspack/webpack plugin shape. Option normalization is factored into src/options.ts (a small d() helper fills in defaults and produces a Required<PluginOptions>), and static asset paths for the runtime/client files live in src/paths.ts. At apply-time, the plugin composes several Rspack primitives rather than owning its own execution model: an EntryPlugin to inject the refresh entry, ProvidePlugin/DefinePlugin to inject the runtime and globals, a module.rules.unshift to add the refresh loader ahead of other rules, and a compilation.hooks.additionalTreeRuntimeRequirements tap to guarantee the module-cache runtime global is present. Because it is a thin adapter directly over Rspack’s plugin/hook API surface, any breaking change to that API (hook names, ProvidePlugin/DefinePlugin semantics) would break the plugin immediately — there is no abstraction layer shielding it.

Tech Stack Written in TypeScript and built/tested with Bytedance’s own rstack tool (rs lib for the library build, rs test for tests, rs lint/rs fmt for linting/formatting, rs hooks wired into the prepare script for git hooks). The package peer-depends on @rspack/core (^2.0.0, optional) and react-refresh (>=0.10.0 <1.0.0), and lives in a pnpm workspace alongside a client/ runtime bundle, a full example/ Rspack + React project, and a test/ suite. Published artifacts are limited to client and dist via the files field, keeping the npm package small.

Code Quality Testing goes beyond unit mocks: test/test.spec.ts runs real Rspack compilations against fixture projects under test/fixtures/ and asserts on the actual emitted JS/CSS output (e.g. checking for $RefreshReg$ in specific bundles), rejecting the test promise on any compilation error or warning. Naming is consistent and the options module is fully typed with JSDoc on every field. CI is configured via .github/ workflows, and lint/format enforcement runs through the rs lint/rs fmt scripts plus a prepare-time git hook — a solid, if compact, quality setup for a package of this size.

API Design The public surface is a single class instantiated the same way any webpack-style plugin is (new ReactRefreshRspackPlugin(options)), works with zero options out of the box, and defaults sensibly (dev-only activation, node_modules excluded, standard JS/TS extensions matched). Advanced options (test/include/exclude/resourceQuery) intentionally reuse Rspack’s own RuleSetCondition shape instead of inventing a new one, so there’s little additional API surface to learn if you already know Rspack’s module rules. The README documents every option with defaults and two runnable examples (SWC loader and babel-loader). The main friction point is that Fast Refresh still requires a second, separate step — enabling jsc.transform.react.refresh (or equivalent) in your loader — that the plugin does not validate or surface if missing.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search