Skip to content

Commit fb5bc4f

Browse files
justin808claude
andcommitted
docs: address RSC pitfalls review follow-ups (#3155)
Applies the documentation review comments surfaced against PR #3087: - Add missing `runs-on: ubuntu-latest` to the CI workflow example in node-renderer/basics.md so the snippet is copy-paste valid. - Correct the `resolve.fallback: false` inline comment in rsc-troubleshooting.md to match the surrounding explanation ("omit the module" rather than "provide empty modules"). - Expand the upgrading-existing-pro-app audit checklist to include the React, React DOM, router, and ReactOnRails hook names that were listed in "What to look for" but missing from the checklist. - Reorder the MessageChannel troubleshooting section to lead with the recommended `additionalContext` fix and demote BannerPlugin to a fallback, so users encounter the preferred option first. - Clarify `fetch` under "Browser APIs": it is a Node.js global since v18 and works in Server Components; only flag it when called inside a `useEffect` (already covered by the hooks list). Fixes #3155 Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
1 parent 4eb8364 commit fb5bc4f

3 files changed

Lines changed: 18 additions & 7 deletions

File tree

docs/oss/building-features/node-renderer/basics.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ The Node Renderer must be started as a background process before running tests.
157157
# .github/workflows/test.yml (GitHub Actions example)
158158
jobs:
159159
test:
160+
runs-on: ubuntu-latest
160161
env:
161162
# Job-level: both the renderer and Rails test steps need this
162163
RENDERER_PASSWORD: ${{ secrets.RENDERER_PASSWORD }}

docs/oss/migrating/rsc-troubleshooting.md

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -863,7 +863,7 @@ externals: {
863863
**Correct approach:**
864864

865865
```js
866-
// In serverWebpackConfig.js -- tells webpack to provide empty modules
866+
// In serverWebpackConfig.js -- tells webpack to omit these modules (no require() call emitted)
867867
resolve: {
868868
fallback: {
869869
path: false,
@@ -881,7 +881,19 @@ resolve: {
881881

882882
**Root cause:** `react-dom/server.browser.js` (used for SSR streaming) instantiates `MessageChannel` at **module load time** for React's internal scheduler. The VM sandbox does not provide `MessageChannel` as a global, and `supportModules` does not include it.
883883

884-
**Fix:** Use webpack's `BannerPlugin` to inject a minimal `MessageChannel` polyfill at the top of the server bundle. The polyfill only needs to support `port2.postMessage` triggering `port1.onmessage`, which is all React's scheduler requires:
884+
**Fix (recommended):** Use `additionalContext` in the node renderer config to inject Node.js' native `MessageChannel` into the VM sandbox. Node.js has had a native `MessageChannel` since Node 15, and passing it through `additionalContext` gives React's scheduler correct async scheduling (macrotask delivery), which is what it depends on:
885+
886+
```js
887+
// In your node-renderer.js config
888+
const { MessageChannel } = require('node:worker_threads');
889+
890+
const config = {
891+
// ... other options
892+
additionalContext: { MessageChannel },
893+
};
894+
```
895+
896+
**Fallback:** If you cannot change the renderer config (e.g., a build-only setup without renderer config control), inject a minimal `MessageChannel` polyfill at the top of the server bundle using webpack's `BannerPlugin`. The polyfill only needs to support `port2.postMessage` triggering `port1.onmessage`, which is all React's scheduler requires:
885897

886898
```js
887899
// In serverWebpackConfig.js
@@ -914,9 +926,7 @@ plugins: [
914926
];
915927
```
916928

917-
This injects the polyfill as raw JavaScript at the top of the bundle output, ensuring `MessageChannel` is defined before any module code executes.
918-
919-
**Recommended approach:** Use `additionalContext` in the renderer config to inject Node.js' native `MessageChannel` into the VM sandbox. This provides correct async scheduling (macrotask delivery), which React's scheduler depends on. The `BannerPlugin` polyfill above delivers messages synchronously — this works for current SSR streaming scenarios but may cause subtle rendering bugs if React yields mid-render and re-enters the scheduler.
929+
This injects the polyfill as raw JavaScript at the top of the bundle output, ensuring `MessageChannel` is defined before any module code executes. Note: this polyfill delivers messages synchronously, which works for current SSR streaming scenarios but may cause subtle rendering bugs if React yields mid-render and re-enters the scheduler. Prefer the `additionalContext` approach above whenever possible.
920930

921931
> **When to use the `BannerPlugin` polyfill:** Use it only when the renderer config's `additionalContext` is not accessible (e.g., in a build-only setup without renderer config control). If you encounter recursive stack-overflow errors or unexpected rendering behavior with the synchronous polyfill, switch to the `additionalContext` approach.
922932

docs/pro/react-server-components/upgrading-existing-pro-app.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ Components that use any of the following **must** have `'use client'`:
4545
- **Router client APIs**: `useNavigate`, `useLocation`, `useParams`
4646
- **SSR entry-point files** using `StaticRouter`: these are SSR wrappers, not RSC server components — see the `.server.jsx` naming collision below
4747
- **Event handlers**: `onClick`, `onChange`, `onSubmit`, etc.
48-
- **Browser APIs**: `window`, `document`, `localStorage`, `fetch` in effects
48+
- **Browser APIs**: `window`, `document`, `localStorage` (note: `fetch` is a Node.js global since v18 and works in Server Components — calling it directly in server context is an encouraged RSC pattern; only flag `fetch` if it is called inside a `useEffect`, which is already covered by the hooks list above)
4949

5050
### The `.server.jsx` naming collision
5151

@@ -71,7 +71,7 @@ There is no warning when a component is auto-classified as a server component. I
7171

7272
Before proceeding to Step 1:
7373

74-
- [ ] Search your component source files for `useState`, `useEffect`, `useContext`, `useSelector`, `useDispatch`, `useTransition`, `useDeferredValue`, `useNavigate`, `useLocation`, `useParams`, `ReactOnRails.getStore`
74+
- [ ] Search your component source files for `useState`, `useEffect`, `useContext`, `useRef`, `useReducer`, `useCallback`, `useMemo`, `useTransition`, `useDeferredValue`, `useId`, `useOptimistic`, `useFormStatus`, `useSelector`, `useDispatch`, `useNavigate`, `useLocation`, `useParams`, `ReactOnRails.getStore`, `ReactOnRails.authenticityToken`
7575
- [ ] Check all `.server.jsx` files -- these almost certainly need `'use client'`
7676
- [ ] Check components that use `StaticRouter` (SSR wrapper, not a client API — but the file likely uses other client APIs)
7777
- [ ] Verify no component relies on browser globals (`window`, `document`) without `'use client'`

0 commit comments

Comments
 (0)