Skip to content

Commit ab27cc0

Browse files
committed
Generate Rspack RSC manifests without webpack plugin
1 parent 600500a commit ab27cc0

11 files changed

Lines changed: 416 additions & 213 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ After a release, run `/update-changelog` in Claude Code to analyze commits, writ
3939
#### Fixed
4040

4141
- **Client-only Vite setups no longer fail Rails boot on Shakapacker's `packageManager` guard**: React on Rails now installs an engine initializer that runs before `shakapacker.manager_checker` and no-ops Shakapacker's `error_unless_package_manager_is_obvious!` when the host app has no Shakapacker config (`config/shakapacker.yml` or `SHAKAPACKER_CONFIG`). This unblocks apps that use the `react-on-rails/client` npm package from an existing Vite entrypoint and do not use the Ruby render helpers. The Ruby helpers that resolve bundle paths still require Shakapacker configuration. Apps with Shakapacker config keep Shakapacker's guard unchanged. Fixes [Issue 3145](https://github.com/shakacode/react_on_rails/issues/3145).
42+
- **[Pro]** **Rspack RSC manifests generated without webpack-only plugin APIs**: RSC-enabled Rspack generator output now includes `rscManifestPlugin.js`, which scans Shakapacker source paths for `'use client'` modules, adds them to the client/server bundle graphs, and emits `react-client-manifest.json` plus `react-server-client-manifest.json` without applying `react-on-rails-rsc/WebpackPlugin` to the Rspack compiler. This avoids the Rspack crash on webpack-only compilation APIs and prevents the Pro Node Renderer from uploading missing RSC manifests. Fixes [Issue 1828](https://github.com/shakacode/react_on_rails/issues/1828).
4243
- **[Pro]** Streaming server-render responses now raise `ReactOnRailsPro::Error` when the stream response status is unavailable or the renderer delivers a readable HTTP error status as a streaming body, instead of silently returning no chunks. This is a user-visible behavior change for callers that do not already rescue `ReactOnRailsPro::Error` from `each_chunk`. [PR 3383](https://github.com/shakacode/react_on_rails/pull/3383).
4344
- **[Pro]** **TanStack Router hydration now supports the current router stores API**: `react-on-rails-pro/tanstack-router` client hydration now uses TanStack Router's current `router.stores.setMatches()` API when `router.__store.setState()` is unavailable, so SSR hydration works with newer `@tanstack/react-router` releases without app-level compatibility shims. Fixes [Issue 3375](https://github.com/shakacode/react_on_rails/issues/3375). [PR 3376](https://github.com/shakacode/react_on_rails/pull/3376) by [justin808](https://github.com/justin808).
4445
- **[Pro]** **TanStack Router hydration no longer double-calls `loadRouteChunk` under React 18 StrictMode**: React 18's StrictMode double-renders components with fresh hook state on each pass, so the `routerRef.current === null` guard in `clientHydrate.ts` fired twice when `options.createRouter` returned the same router instance, re-running `loadRouteChunk`, `__store.setState`, and the user-defined `hydrate` callback. The render-phase init is now memoized via a module-level `WeakMap` keyed on the router instance, dedup'ing per-router side effects across mount cycles. Production behavior is unchanged because each mount creates a fresh router. Fixes [Issue 3405](https://github.com/shakacode/react_on_rails/issues/3405). [PR 3410](https://github.com/shakacode/react_on_rails/pull/3410) by [justin808](https://github.com/justin808).
Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Rspack Compatibility with React Server Components
22

3-
> **Status**: Experimental — generator support is complete; runtime verification is in progress.
3+
> **Status**: Experimental — generated Rspack configs now emit the RSC manifests; full Pro Node Renderer hydration verification is still in progress.
44
55
This page documents the compatibility status of [Rspack](https://rspack.dev/) with React on Rails Pro's React Server Components (RSC) implementation.
66

@@ -11,25 +11,34 @@ The generator already supports Rspack — when `assets_bundler: rspack` is detec
1111

1212
The RSC implementation depends on the `react-on-rails-rsc` npm package, which provides:
1313

14-
- **WebpackPlugin** — generates client/server component manifest files
14+
- **WebpackPlugin** — generates client/server component manifest files for webpack builds
1515
- **WebpackLoader** — transforms `'use client'` files into client reference proxies in the RSC bundle
1616

17+
For Rspack builds, generated configs use `config/rspack/rscManifestPlugin.js` instead of applying
18+
`react-on-rails-rsc/WebpackPlugin` directly. The helper scans Shakapacker source paths for `'use client'`
19+
modules, adds those modules to the client and server bundle graphs, and emits the two manifest files
20+
expected by the Pro Node Renderer:
21+
22+
- `react-client-manifest.json`
23+
- `react-server-client-manifest.json`
24+
1725
## Compatibility Matrix
1826

19-
| Component | Rspack Compatible | Notes |
20-
| ---------------------------------------------------------------- | ------------------ | ------------------------------------------------------------------------------- |
21-
| **RSC bundle config** (`rscWebpackConfig.js`) | Yes | Does not use WebpackPlugin; only uses loader + resolve settings |
22-
| **WebpackLoader** (`react-on-rails-rsc/WebpackLoader`) | Yes | Standard loader interface (`this.resourcePath`, source transform) |
23-
| **`conditionNames: ['react-server', '...']`** | Yes | Rspack supports conditional exports resolution |
24-
| **`resolve.alias`** (`react-dom/server: false`) | Yes | Rspack supports alias to `false` |
25-
| **`LimitChunkCountPlugin`** | Yes | Generated configs use bundler-agnostic `bundler.optimize.LimitChunkCountPlugin` |
26-
| **Loader chain (SWC + Babel)** | Yes | Generated config handles both `function` and `Array` `rule.use` styles |
27-
| **WebpackPlugin** (`react-on-rails-rsc/WebpackPlugin`) | Needs verification | Uses webpack internal APIs (see below) |
28-
| **Three-bundle build** (`RSC_BUNDLE_ONLY`, `SERVER_BUNDLE_ONLY`) | Yes | Environment variable routing is bundler-agnostic |
27+
| Component | Rspack Compatible | Notes |
28+
| ---------------------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------- |
29+
| **RSC bundle config** (`rscWebpackConfig.js`) | Yes | Does not use WebpackPlugin; only uses loader + resolve settings |
30+
| **WebpackLoader** (`react-on-rails-rsc/WebpackLoader`) | Yes | Standard loader interface (`this.resourcePath`, source transform) |
31+
| **`conditionNames: ['react-server', '...']`** | Yes | Rspack supports conditional exports resolution |
32+
| **`resolve.alias`** (`react-dom/server: false`) | Yes | Rspack supports alias to `false` |
33+
| **`LimitChunkCountPlugin`** | Yes | Generated configs use bundler-agnostic `bundler.optimize.LimitChunkCountPlugin` |
34+
| **Loader chain (SWC + Babel)** | Yes | Generated config handles both `function` and `Array` `rule.use` styles |
35+
| **WebpackPlugin** (`react-on-rails-rsc/WebpackPlugin`) | Webpack only | Uses webpack internal APIs (see below) |
36+
| **Rspack manifest helper** (`rscManifestPlugin.js`) | Yes | Emits the Pro RSC client/server manifests without webpack-only plugin APIs |
37+
| **Three-bundle build** (`RSC_BUNDLE_ONLY`, `SERVER_BUNDLE_ONLY`) | Yes | Environment variable routing is bundler-agnostic |
2938

3039
## WebpackPlugin Compatibility Details
3140

32-
The `RSCWebpackPlugin` is the critical compatibility question. It wraps React's
41+
The `RSCWebpackPlugin` remains the webpack path. It wraps React's
3342
`react-server-dom-webpack/plugin`, which uses these webpack internal APIs:
3443

3544
- `webpack/lib/dependencies/ModuleDependency` — base class for dependency tracking
@@ -44,14 +53,14 @@ The `RSCWebpackPlugin` is the critical compatibility question. It wraps React's
4453
- `compilation.chunkGraph` — chunk/module graph traversal
4554

4655
**Why this matters**: The plugin generates `react-client-manifest.json` and
47-
`react-ssr-manifest.json`, which map client component file paths to their chunk
56+
`react-server-client-manifest.json`, which map client component file paths to their chunk
4857
IDs and bundle filenames. Without these manifests, the RSC runtime cannot resolve
4958
`'use client'` component references during streaming.
5059

51-
**Rspack v2 compatibility**: Rspack v2 has significantly improved webpack plugin
52-
compatibility. The [Rspack team has confirmed](https://github.com/shakacode/react_on_rails/issues/1828#issuecomment-3350629010)
53-
that Rspack supports RSC with the JavaScript API. However, runtime verification
54-
with the specific `react-on-rails-rsc` plugin is still needed.
60+
**Rspack v2 compatibility**: Applying this webpack plugin directly to Rspack is not supported.
61+
Rspack does not expose every webpack compiler and compilation object used by the plugin. Generated
62+
Rspack configs therefore route manifest generation through `rscManifestPlugin.js`, which uses Rspack's
63+
public compilation hooks and keeps the existing `react-on-rails-rsc` wire protocol.
5564

5665
## How the RSC Bundle Avoids the Plugin
5766

@@ -63,7 +72,8 @@ which skips adding `RSCWebpackPlugin`. The RSC bundle only uses:
6372
3. **Aliases** to exclude `react-dom/server` from the RSC bundle
6473

6574
This means the RSC bundle itself should work with Rspack today. The plugin is only
66-
added to the **server** and **client** bundles for manifest generation.
75+
added to the **server** and **client** bundles for manifest generation when using webpack. With Rspack,
76+
the generated manifest helper is added to those bundles instead.
6777

6878
## Testing with Rspack
6979

@@ -74,27 +84,29 @@ To test RSC with Rspack in your project:
7484
3. Verify configs are in `config/rspack/`
7585
4. Build all three bundles and check for:
7686
- `rsc-bundle.js` in the output
77-
- `react-client-manifest.json` and `react-ssr-manifest.json` (from the plugin)
87+
- `react-client-manifest.json`
88+
- `react-server-client-manifest.json`
7889
- No webpack/Rspack compilation errors
7990

8091
## Known Limitations
8192

82-
1. **No `react-server-dom-rspack` package**: React does not ship a dedicated Rspack
83-
variant of the RSC wire protocol. The `react-server-dom-webpack` package is used,
84-
relying on Rspack's webpack compatibility layer.
93+
1. **Runtime support remains experimental**: The generated Rspack helper emits the manifests
94+
expected by React on Rails Pro, but each app should still verify an interactive `'use client'`
95+
boundary through the Pro Node Renderer before treating Rspack + RSC as production-ready.
8596

8697
2. **Plugin `require('webpack')` call**: The `react-server-dom-webpack/plugin`
8798
internally calls `require('webpack')`, which loads webpack even in Rspack projects.
88-
Rspack's compatibility layer must intercept the compiler/compilation interactions
89-
for the plugin to function correctly.
99+
Generated Rspack configs avoid that direct plugin path and use `rscManifestPlugin.js`.
90100

91-
3. **No official React Rspack support**: The React team has not officially tested or
92-
endorsed `react-server-dom-webpack` with Rspack. Compatibility is provided by
93-
Rspack's webpack API compatibility layer.
101+
3. **Native Rspack RSC is a future migration path**: Rspack documents native RSC support through
102+
`@rspack/core`'s `experiments.rsc` APIs and `react-server-dom-rspack`, but that path currently
103+
targets React 19.1+ and a different manifest/runtime shape. React on Rails Pro's current RSC
104+
integration stays on the `react-on-rails-rsc` protocol for React 19.0.x compatibility.
94105

95106
## Related Resources
96107

97108
- [Issue #1828: Rspack support for RSC](https://github.com/shakacode/react_on_rails/issues/1828)
98109
- [Rspack RSC support PR](https://github.com/web-infra-dev/rspack/pull/5824)
110+
- [Rspack React Server Components guide](https://v2.rspack.rs/guide/tech/rsc)
99111
- [Three-bundle architecture](./how-react-server-components-work.md)
100112
- [Upgrading an existing Pro app to RSC](./upgrading-existing-pro-app.md)

react_on_rails/lib/generators/react_on_rails/base_generator.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ class BaseGenerator < Rails::Generators::Base
6464
"development.js" => "base/base/config/webpack/development.js.tt",
6565
"production.js" => "base/base/config/webpack/production.js.tt",
6666
"serverWebpackConfig.js" => "base/base/config/webpack/serverWebpackConfig.js.tt",
67+
"rscManifestPlugin.js" => "base/base/config/webpack/rscManifestPlugin.js.tt",
6768
"rscWebpackConfig.js" => "rsc/base/config/webpack/rscWebpackConfig.js.tt",
6869
"ServerClientOrBoth.js" => "base/base/config/webpack/ServerClientOrBoth.js.tt",
6970
# Legacy filename generated by older React on Rails versions.
@@ -229,6 +230,7 @@ def copy_webpack_config
229230
config/webpack/production.js
230231
config/webpack/serverWebpackConfig.js
231232
config/webpack/ServerClientOrBoth.js]
233+
base_files << "config/webpack/rscManifestPlugin.js" if use_rsc?
232234
config = { message: DOCS_REFERENCE_MESSAGE }
233235
base_files.each do |file|
234236
template("#{base_path}/#{file}.tt", destination_config_path(file), config)

react_on_rails/lib/generators/react_on_rails/rsc/USAGE

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ Example:
1313
- RSC bundle watcher to Procfile.dev
1414

1515
Modifies:
16-
- config/webpack/serverWebpackConfig.js (adds RSCWebpackPlugin, rscBundle param)
17-
- config/webpack/clientWebpackConfig.js (adds RSCWebpackPlugin)
16+
- config/webpack/rscManifestPlugin.js (adds RSC manifest generation helper)
17+
- config/webpack/serverWebpackConfig.js (adds RSC manifest helper, rscBundle param)
18+
- config/webpack/clientWebpackConfig.js (adds RSC manifest helper)
1819
- config/webpack/ServerClientOrBoth.js (adds rscWebpackConfig, RSC_BUNDLE_ONLY)
1920
- config/initializers/react_on_rails_pro.rb (adds RSC config block)
2021
- package.json (adds react-on-rails-rsc)

0 commit comments

Comments
 (0)