Skip to content

Commit 6429ce0

Browse files
justin808claude
andcommitted
Add RSC and Node Renderer documentation to CLAUDE.md and README.md
Document the three-bundle architecture (client, server SSR, RSC), Node Renderer setup requirements, VM sandbox constraints, RSC component classification rules, and common troubleshooting patterns. Update README with current version targets, RSC section, expanded config file list, and all six Procfile.dev processes. Motivated by the debugging challenges in PR #723 where undocumented constraints (VM sandbox lacks require/MessageChannel, 'use client' classification, Node renderer must run for tests) caused significant debugging time. Filed shakacode/react_on_rails#3076 with upstream doc suggestions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 248ba65 commit 6429ce0

2 files changed

Lines changed: 135 additions & 16 deletions

File tree

CLAUDE.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,76 @@ bundle exec rubocop
1616
bundle exec rubocop -a
1717
```
1818

19+
## Architecture: Three Bundle System
20+
21+
This project builds **three separate Rspack bundles** via Shakapacker:
22+
23+
| Bundle | Config | Output | Runtime |
24+
|--------|--------|--------|---------|
25+
| **Client** | `clientWebpackConfig.js` | `public/packs/` | Browser |
26+
| **Server (SSR)** | `serverWebpackConfig.js` | `ssr-generated/` | Node renderer VM sandbox |
27+
| **RSC** | `rscWebpackConfig.js` | RSC output dir | Node renderer (full Node.js) |
28+
29+
### Key constraints
30+
31+
- The **server bundle runs in a VM sandbox** (`vm.createContext()`), NOT full Node.js. It lacks `require`, `MessageChannel`, `TextEncoder`, and other Node/browser globals.
32+
- Use `resolve.fallback: false` (NOT `externals`) for Node builtins in the server bundle — the VM has no `require()`, so externalized `require('path')` calls will crash.
33+
- The **RSC bundle** targets Node.js directly with the `react-server` condition, so Node builtins work normally there.
34+
- A `BannerPlugin` injects a `MessageChannel` polyfill into the server bundle because `react-dom/server.browser` needs it at module load time.
35+
36+
### Build commands
37+
38+
```bash
39+
# Build only the server bundle
40+
SERVER_BUNDLE_ONLY=yes bin/shakapacker
41+
42+
# Build only the RSC bundle
43+
RSC_BUNDLE_ONLY=true bin/shakapacker
44+
45+
# Watch modes (used by Procfile.dev)
46+
SERVER_BUNDLE_ONLY=yes bin/shakapacker --watch
47+
RSC_BUNDLE_ONLY=true bin/shakapacker --watch
48+
```
49+
50+
## Node Renderer
51+
52+
React on Rails Pro's NodeRenderer must be running for SSR and RSC payload generation.
53+
54+
- **Port**: 3800 (configured in `config/initializers/react_on_rails_pro.rb`)
55+
- **Launcher**: `node react-on-rails-pro-node-renderer.js`
56+
- **Auth**: Requires `RENDERER_PASSWORD` env var (defaults to `local-dev-renderer-password` in dev/test, required with no fallback in production)
57+
- **Started automatically** by `bin/dev` / `Procfile.dev`
58+
59+
### Testing requirement
60+
61+
**The Node renderer must be running before `bundle exec rspec`.** Tests will fail with `Net::ReadTimeout` if it is not running. CI starts it as a background process and waits up to 30 seconds for TCP readiness on port 3800.
62+
63+
### Enabled environments
64+
65+
The renderer is enabled when `Rails.env.local?` (development + test) or `REACT_USE_NODE_RENDERER=true`. Production requires explicit env var configuration.
66+
67+
## RSC Component Classification
68+
69+
React on Rails Pro auto-classifies components based on the `'use client'` directive:
70+
71+
- **With `'use client'`** → registered via `ReactOnRails.register()` (traditional SSR/client component)
72+
- **Without `'use client'`** → registered via `registerServerComponent()` (React Server Component)
73+
74+
### Common gotcha: `.server.jsx` files
75+
76+
In this codebase, `.server.jsx` does NOT mean "React Server Component" — it means traditional **server-side rendering** (e.g., `StaticRouter`). If a `.server.jsx` file uses client APIs like `ReactOnRails.getStore()` or React hooks, it **needs** the `'use client'` directive to prevent RSC misclassification. The naming predates RSC and refers to the Rails SSR render path.
77+
78+
### Key files
79+
80+
| File | Purpose |
81+
|------|---------|
82+
| `config/webpack/rscWebpackConfig.js` | RSC bundle configuration |
83+
| `config/webpack/rspackRscPlugin.js` | Detects `'use client'` directives, emits React Flight manifests |
84+
| `client/app/packs/rsc-bundle.js` | RSC entry point |
85+
| `client/app/packs/rsc-client-components.js` | Client component registration for RSC |
86+
| `config/initializers/react_on_rails_pro.rb` | Node renderer + RSC configuration |
87+
| `react-on-rails-pro-node-renderer.js` | Node renderer launcher |
88+
1989
## Conductor Compatibility (Version Managers)
2090

2191
### Problem

README.md

Lines changed: 65 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -83,19 +83,19 @@ You can see this tutorial live here: [http://reactrails.com/](http://reactrails.
8383
+ [Configuration Files](#configuration-files)
8484
+ [Additional Resources](#additional-resources)
8585
+ [Thruster HTTP/2 Proxy](#thruster-http2-proxy)
86+
+ [React Server Components (RSC)](#react-server-components-rsc)
8687
+ [Sass, CSS Modules, and Tailwind CSS integration](#sass-css-modules-and-tailwind-css-integration)
8788
+ [Fonts with SASS](#fonts-with-sass)
8889
+ [Process Management during Development](#process-management-during-development)
89-
+ [Rendering with Express Server](#rendering-with-express-server)
90-
+ [Setup](#setup)
9190
+ [Contributors](#contributors)
9291
+ [About ShakaCode](#about-shakacode)
9392
+ [RubyMine and WebStorm](#rubymine-and-webstorm)
9493
+ [Open Code of Conduct](#open-code-of-conduct)
9594

9695
## Demoed Functionality
9796

98-
- Example of using the [react_on_rails gem](https://github.com/shakacode/react_on_rails) for easy React + Rspack integration with Rails.
97+
- Example of using [React on Rails Pro](https://www.shakacode.com/react-on-rails-pro/) with the NodeRenderer for server-side rendering.
98+
- Example of [React Server Components (RSC)](#react-server-components-rsc) with streaming and selective hydration.
9999
- Example of React with [CSS Modules](http://glenmaddern.com/articles/css-modules) inside Rails using modern Shakapacker/Rspack builds.
100100
- Example of enabling hot reloading of both JS and CSS (modules) from your Rails app in development mode. Change your code. Save. Browser updates without a refresh!
101101
- Example of React/Redux with Rails Action Cable.
@@ -110,18 +110,17 @@ You can see this tutorial live here: [http://reactrails.com/](http://reactrails.
110110

111111
See package.json and Gemfile for versions
112112

113-
1. [react_on_rails gem](https://github.com/shakacode/react_on_rails/)
114-
1. [React](http://facebook.github.io/react/)
113+
1. [React on Rails Pro](https://www.shakacode.com/react-on-rails-pro/) with NodeRenderer for SSR and React Server Components
114+
1. [React 19](http://facebook.github.io/react/) with React Server Components support
115115
1. [Redux](https://github.com/reactjs/redux)
116116
1. [react-router](https://github.com/reactjs/react-router)
117117
1. [react-router-redux](https://github.com/reactjs/react-router-redux)
118118
1. [Rspack with hot-reload](https://rspack.dev/guide/features/dev-server) (for local dev)
119-
1. [Babel transpiler](https://github.com/babel/babel)
119+
1. [SWC transpiler](https://swc.rs/) for fast JavaScript/TypeScript compilation
120120
1. [Ruby on Rails 8](http://rubyonrails.org/) for backend app and comparison with plain HTML
121121
1. [Thruster](https://github.com/basecamp/thruster) - Zero-config HTTP/2 proxy for optimized asset delivery
122122
1. [Heroku deployment guide](https://devcenter.heroku.com/articles/getting-started-with-rails8)
123123
1. [Deployment to the ControlPlane](.controlplane/readme.md)
124-
1. [Turbolinks 5](https://github.com/turbolinks/turbolinks)
125124
1. [Tailwind CSS](https://github.com/tailwindlabs/tailwindcss)
126125

127126
## Basic Demo Setup
@@ -183,10 +182,10 @@ assets_bundler: rspack
183182

184183
### Version Targets
185184

186-
- `react_on_rails` gem: `16.4.0`
187-
- `react-on-rails` npm package: `16.4.0`
188-
- `shakapacker` gem/npm package: `9.7.0`
189-
- `@rspack/core` and `@rspack/cli`: `2.0.0-beta.7` (latest published v2 prerelease at the time of this update)
185+
- `react_on_rails_pro` gem: `16.6.0.rc.0`
186+
- `react-on-rails-pro` npm package: `16.6.0-rc.0`
187+
- `shakapacker` gem/npm package: `10.0.0-rc.0`
188+
- `@rspack/core` and `@rspack/cli`: `2.0.0-beta.7`
190189

191190
### Why Rspack
192191

@@ -197,10 +196,13 @@ assets_bundler: rspack
197196
### Configuration Files
198197

199198
All bundler configuration is in `config/webpack/`:
200-
- `webpackConfig.js` - Main Shakapacker entry point
199+
- `webpackConfig.js` - Main Shakapacker entry point (routes to client, server, or RSC config)
201200
- `commonWebpackConfig.js` - Shared configuration
202-
- `clientWebpackConfig.js` - Client bundle settings
203-
- `serverWebpackConfig.js` - Server-side rendering bundle
201+
- `clientWebpackConfig.js` - Client bundle settings (browser, with HMR)
202+
- `serverWebpackConfig.js` - Server-side rendering bundle (runs in Node renderer VM sandbox)
203+
- `rscWebpackConfig.js` - React Server Components bundle (runs in Node renderer, full Node.js)
204+
- `rspackRscPlugin.js` - Custom plugin that detects `'use client'` directives and emits React Flight manifests
205+
- `bundlerUtils.js` - Shared bundler detection utilities
204206
- `development.js`, `production.js`, `test.js` - Environment-specific settings
205207

206208
### Additional Resources
@@ -243,6 +245,47 @@ The server automatically benefits from HTTP/2, caching, and compression without
243245

244246
For detailed information, troubleshooting, and advanced configuration options, see [docs/thruster.md](docs/thruster.md).
245247

248+
## React Server Components (RSC)
249+
250+
This project demonstrates React Server Components with React on Rails Pro. Visit `/server-components` to see the demo page.
251+
252+
### How It Works
253+
254+
The app builds **three separate bundles**:
255+
256+
1. **Client bundle** — Browser JavaScript with HMR, built by `clientWebpackConfig.js`
257+
2. **Server bundle** — Traditional SSR, runs in the Node renderer's VM sandbox, built by `serverWebpackConfig.js`
258+
3. **RSC bundle** — React Server Components with the `react-server` condition, runs in full Node.js, built by `rscWebpackConfig.js`
259+
260+
The [React on Rails Pro NodeRenderer](https://www.shakacode.com/react-on-rails-pro/) runs on port 3800 and handles both SSR rendering and RSC payload generation. A custom `RspackRscPlugin` detects `'use client'` directives in source files and emits the React Flight manifests (`react-client-manifest.json`, `react-server-client-manifest.json`) that React uses to resolve client component references during streaming.
261+
262+
### Key Files
263+
264+
| File | Purpose |
265+
|------|---------|
266+
| `config/webpack/rscWebpackConfig.js` | RSC bundle configuration |
267+
| `config/webpack/rspackRscPlugin.js` | `'use client'` detection and manifest generation |
268+
| `client/app/packs/rsc-bundle.js` | RSC entry point — registers server components |
269+
| `client/app/packs/rsc-client-components.js` | Client component registration for RSC hydration |
270+
| `config/initializers/react_on_rails_pro.rb` | NodeRenderer and RSC configuration |
271+
| `react-on-rails-pro-node-renderer.js` | Node renderer launcher (port 3800, 3 workers) |
272+
| `client/app/bundles/server-components/` | RSC demo components |
273+
274+
### Troubleshooting
275+
276+
| Symptom | Cause | Fix |
277+
|---------|-------|-----|
278+
| `Net::ReadTimeout` in tests | Node renderer not running | Start it with `bin/dev` or `node react-on-rails-pro-node-renderer.js` |
279+
| Component renders blank/empty div | Missing `'use client'` directive | Add `'use client'` to components using client APIs (hooks, stores, event handlers) |
280+
| `MessageChannel is not defined` | Server bundle VM missing polyfill | Check `BannerPlugin` in `serverWebpackConfig.js` |
281+
| `require is not defined` | Server bundle using `externals` | Use `resolve.fallback: false` instead — the VM sandbox has no `require()` |
282+
| RSC manifests missing | RSC bundle not built | Run `RSC_BUNDLE_ONLY=true bin/shakapacker` |
283+
284+
### Further Reading
285+
286+
- [React on Rails Pro RSC Documentation](https://www.shakacode.com/react-on-rails-pro/)
287+
- [React Server Components RFC](https://github.com/reactjs/rfcs/blob/main/text/0188-server-components.md)
288+
246289
## Sass, CSS Modules, and Tailwind CSS Integration
247290
This example project uses mainly Tailwind CSS for styling.
248291
Besides this, it also demonstrates Sass and CSS modules, particularly for some CSS transitions.
@@ -270,12 +313,18 @@ export default class CommentBox extends React.Component {
270313
### Fonts with SASS
271314
The tutorial makes use of a custom font OpenSans-Light. We're doing this to show how to add assets for the CSS processing. The font files are located under [client/app/assets/fonts](client/app/assets/fonts) and are loaded by both the Rails asset pipeline and the Rspack HMR server.
272315

273-
## Process management during development
316+
## Process Management during Development
274317
```bash
275318
bundle exec foreman start -f <Procfile>
276319
```
277320

278-
1. [`Procfile.dev`](Procfile.dev): Starts the Rspack Dev Server and Rails with Hot Reloading.
321+
1. [`Procfile.dev`](Procfile.dev): Starts all development processes with Hot Reloading:
322+
- `rescript` — ReScript watch mode
323+
- `rails` — Rails server via Thruster on port 3000
324+
- `wp-client` — Client Rspack dev server with HMR
325+
- `wp-server` — Server Rspack watcher for SSR bundle
326+
- `wp-rsc` — RSC Rspack watcher for React Server Components bundle
327+
- `node-renderer` — React on Rails Pro Node renderer on port 3800
279328
1. [`Procfile.dev-static`](Procfile.dev-static): Starts the Rails server and generates static assets that are used for tests.
280329

281330
## Contributors

0 commit comments

Comments
 (0)