Skip to content

Commit b4ea8a9

Browse files
authored
Gate TanStack starter on React on Rails 17 rc7 (#185)
* Gate TanStack starter on React on Rails 17 rc7 * Refresh RSC showcase visual snapshot * Use neutral RSC prefetch completion copy * Prepare assets for Playwright smoke * Assert RSC prefetch response
1 parent 7baabc9 commit b4ea8a9

18 files changed

Lines changed: 155 additions & 65 deletions

AGENTS.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@ code first and then update the docs or this file in the same change.
2727
shell and Pro RSC payload endpoint; a bare TanStack Router loader selects the
2828
server component and props, and the exported React on Rails Pro `RSCRoute`
2929
helper fetches/composes the RSC payload with client islands on the local
30-
Rspack default.
30+
Rspack default. The route also includes a provider-backed RSC prefetch action
31+
through `prefetchServerComponent`.
3132
- `/hello_server` demonstrates streaming RSC. Rspack client-reference manifests
32-
are available with `react-on-rails-rsc@19.0.5-rc.7`; the remaining strict
33+
are available with `react-on-rails-rsc@19.2.1-rc.0`; the remaining strict
3334
production CSP hydration limitation is documented in
3435
`docs/11-rsc-csp-nonce-spike.md`.
3536
- The root path `/` is a public Rails landing page (`home#index`). It leads with
@@ -225,6 +226,9 @@ Rules:
225226
fetching/rendering. Do not recreate the Pro length-prefixed stream parser,
226227
import `react-on-rails-rsc/client.browser` directly from app code, or replace
227228
the payload with a bespoke JSON protocol when the goal is to demonstrate RSC.
229+
- Use `react-on-rails-pro/prefetchServerComponent` when exercising provider
230+
cache prefetching for this route; keep it pointed at the same component name
231+
and props selected by the route loader.
228232
- Keep `app/views/react_on_rails_pro/rsc_payload.text.erb` trim-safe. A trailing
229233
newline after the helper output creates a blank line between length-prefixed
230234
RSC chunks, which the current Pro client parser treats as malformed. Track
@@ -275,7 +279,7 @@ Rules:
275279
- Production-assets mode must run with optimized assets and the React on Rails
276280
Pro Node renderer.
277281
- Treat interactive RSC client-reference manifest generation on Rspack as
278-
expected behavior with `react-on-rails-rsc@19.0.5-rc.7` or newer. Keep
282+
expected behavior with `react-on-rails-rsc@19.2.1-rc.0` or newer. Keep
279283
`pnpm run repro:rspack-rsc` available as the small regression check.
280284
- When validating unreleased `react_on_rails` or `react_on_rails_rsc` branches,
281285
follow `docs/12-upstream-branch-testing.md`. Do not leave local path or git

Gemfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@ group :test do
6565
gem "selenium-webdriver"
6666
end
6767

68-
gem "react_on_rails", "17.0.0.rc.6"
68+
gem "react_on_rails", "17.0.0.rc.7"
6969

70-
gem "react_on_rails_pro", "17.0.0.rc.6"
70+
gem "react_on_rails_pro", "17.0.0.rc.7"
7171

7272
gem "shakapacker", "10.2.0"
7373

Gemfile.lock

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -330,22 +330,21 @@ GEM
330330
prism (>= 1.6.0)
331331
rbs (>= 4.0.0)
332332
tsort
333-
react_on_rails (17.0.0.rc.6)
333+
react_on_rails (17.0.0.rc.7)
334334
addressable
335335
connection_pool
336336
execjs (~> 2.5)
337337
rails (>= 5.2)
338338
rainbow (~> 3.0)
339339
shakapacker (>= 6.0)
340-
react_on_rails_pro (17.0.0.rc.6)
341-
addressable
340+
react_on_rails_pro (17.0.0.rc.7)
342341
async (>= 2.29)
343342
async-http (~> 0.95)
344343
execjs (~> 2.9)
345344
io-endpoint (~> 0.17.0)
346345
jwt (>= 2.5, < 4)
347-
rainbow
348-
react_on_rails (= 17.0.0.rc.6)
346+
nokogiri (>= 1.12, < 2)
347+
react_on_rails (= 17.0.0.rc.7)
349348
regexp_parser (2.12.0)
350349
reline (0.6.3)
351350
io-console (~> 0.5)
@@ -492,8 +491,8 @@ DEPENDENCIES
492491
puma (>= 5.0)
493492
rack-attack (~> 6.8)
494493
rails (~> 8.1.3)
495-
react_on_rails (= 17.0.0.rc.6)
496-
react_on_rails_pro (= 17.0.0.rc.6)
494+
react_on_rails (= 17.0.0.rc.7)
495+
react_on_rails_pro (= 17.0.0.rc.7)
497496
rspec-rails (~> 8.0)
498497
rubocop-rails-omakase
499498
selenium-webdriver

README.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ Each route demonstrates a deliberate rendering choice, not a fallback:
5050
Flight stream, and composes that server-streamed tree beside ordinary client
5151
React. It now includes an in-app status panel that separates the working RSC
5252
payload route from the lower-level streaming client-island limitation. It runs
53-
on the default Rspack bundler with `react-on-rails-rsc@19.0.5-rc.7`.
53+
on the default Rspack bundler with `react-on-rails-rsc@19.2.1-rc.0` and
54+
exercises the provider-backed RSC prefetch path.
5455
- **`/hello_server`** demonstrates streaming React Server Components. The demo
5556
keeps an interactive `LikeButton` client island inside a server-rendered
5657
tree, but labels that island as the separate client-reference edge case. On
@@ -77,20 +78,21 @@ Rspack is the default Shakapacker bundler for local development and the
7778
deployed image. Use `config/shakapacker.yml`, `config/rspack/`, and the
7879
`.controlplane/Dockerfile` build ARG as the source of truth for the default
7980
path. The Rspack RSC plugin in
80-
`react-on-rails-rsc@19.0.5-rc.7` emits the client/server manifests the Pro RSC
81+
`react-on-rails-rsc@19.2.1-rc.0` emits the client/server manifests the Pro RSC
8182
client-reference path needs. Webpack remains an opt-in bridge/comparison path
8283
via `SHAKAPACKER_ASSETS_BUNDLER=webpack`.
8384

8485
| Component | Version |
8586
| --- | --- |
86-
| React on Rails / Pro | `17.0.0.rc.3` |
87-
| Shakapacker / Shakapacker Rspack | `10.1.0` |
88-
| React | `19.0.6` |
87+
| React on Rails / Pro | `17.0.0.rc.7` / `17.0.0-rc.7` |
88+
| React on Rails RSC | `19.2.1-rc.0` |
89+
| Shakapacker / Shakapacker Rspack | `10.2.0` |
90+
| React | `19.2.7` |
8991
| Rails | `8.1.x` |
9092
| Language / tooling | TypeScript, pnpm |
9193

92-
Shakapacker stays on `10.1.0` for this release because public `11.1.0`
93-
artifacts are not yet visible in the registries consumed by the starter.
94+
Shakapacker stays on `10.2.0` for this release; the React on Rails 17 RC7
95+
upgrade did not require a coupled Shakapacker bump.
9496

9597
## Setup
9698

SPIKE.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ The remaining limitation is separate from the bundler: `/hello_server`'s client
1515
island still waits on the upstream React on Rails Pro streaming CSP nonce fix in
1616
strict production CSP mode. See `docs/11-rsc-csp-nonce-spike.md`.
1717

18-
The app was bootstrapped with `create-react-on-rails-app --rsc --rspack --package-manager pnpm` and the current release stack targets React on Rails Pro `17.0.0-rc.3` with Shakapacker `10.1.0`. Shakapacker stays on `10.1.0` because public `11.1.0` artifacts are not visible in the registries consumed by the starter. The Rails/Rspack/React on Rails Pro setup passes `react_on_rails:doctor` with the expected warning that both Rspack and Webpack configs are present.
18+
The app was bootstrapped with `create-react-on-rails-app --rsc --rspack --package-manager pnpm` and the current release stack targets React on Rails Pro `17.0.0-rc.7`, React on Rails RSC `19.2.1-rc.0`, and Shakapacker `10.2.0`. Shakapacker stays on `10.2.0` because the React on Rails 17 RC7 upgrade did not require a coupled Shakapacker bump. The Rails/Rspack/React on Rails Pro setup passes `react_on_rails:doctor` with the expected warning that both Rspack and Webpack configs are present.
1919

2020
## Validation Goals
2121

@@ -27,8 +27,9 @@ The app was bootstrapped with `create-react-on-rails-app --rsc --rspack --packag
2727
## What Worked
2828

2929
- Rails 8.1 app scaffolded with PostgreSQL and SolidQueue.
30-
- React on Rails and React on Rails Pro release stack: `17.0.0.rc.3`.
31-
- Shakapacker launch stack: `10.1.0`.
30+
- React on Rails and React on Rails Pro release stack: `17.0.0.rc.7`.
31+
- React on Rails RSC stack: `19.2.1-rc.0`.
32+
- Shakapacker launch stack: `10.2.0`.
3233
- Rspack builds complete successfully and emit the RSC client-reference
3334
manifests with `react-on-rails-rsc@19.0.5-rc.2`.
3435
- The `/dashboard` TanStack Router, Query, and Table surface prerenders through React on Rails Pro's Node renderer and hydrates under the Rails shell.
@@ -45,10 +46,10 @@ manifests present.
4546
Current stance:
4647

4748
- Keep Rspack as the bundler.
48-
- Keep the React on Rails Pro `17.0.0-rc.3` and Shakapacker `10.1.0` release stack.
49+
- Keep the React on Rails Pro `17.0.0-rc.7`, React on Rails RSC `19.2.1-rc.0`, and Shakapacker `10.2.0` release stack.
4950
- Keep the Rspack/RSC client boundary repro in `pnpm run repro:rspack-rsc`.
5051
- Keep direct Rspack packages aligned with the Shakapacker Rspack 2 adapter: `@rspack/core` / `@rspack/cli` `2.0.4` and `@rspack/dev-server` `2.0.1`.
51-
- Keep `react-on-rails-rsc` on `19.0.5-rc.7` or newer when requiring Rspack RSC
52+
- Keep `react-on-rails-rsc` on `19.2.1-rc.0` or newer when requiring Rspack RSC
5253
client-reference manifests.
5354

5455
Impact:

app/javascript/src/RscShowcase/ror_components/RscShowcaseApp.tsx

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import React, { Suspense, useMemo, useState } from 'react';
3+
import React, { Suspense, useCallback, useMemo, useRef, useState } from 'react';
44
import {
55
Outlet,
66
RouterProvider,
@@ -12,6 +12,7 @@ import {
1212
} from '@tanstack/react-router';
1313
import { CheckCircle2, ExternalLink, RefreshCw, Route, Server, ShieldCheck, TriangleAlert } from 'lucide-react';
1414
import RSCRoute from 'react-on-rails-pro/RSCRoute';
15+
import { prefetchServerComponent } from 'react-on-rails-pro/prefetchServerComponent';
1516
import wrapServerComponentRenderer from 'react-on-rails-pro/wrapServerComponentRenderer/client';
1617
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
1718
import { Badge } from '@/components/ui/badge';
@@ -83,6 +84,55 @@ function PayloadRenderer({ routeData }: { routeData: RscRouteData }) {
8384
);
8485
}
8586

87+
type PrefetchStatus = 'idle' | 'loading' | 'ready' | 'error';
88+
89+
function PayloadPrefetchButton({ routeData }: { routeData: RscRouteData | null }) {
90+
const [status, setStatus] = useState<PrefetchStatus>('idle');
91+
const inFlightRef = useRef<Promise<void> | null>(null);
92+
93+
const prefetchPayload = useCallback(() => {
94+
if (!routeData) return Promise.resolve();
95+
if (inFlightRef.current) return inFlightRef.current;
96+
97+
setStatus('loading');
98+
const request = prefetchServerComponent(routeData.componentName, routeData.componentProps, {
99+
skipIfEmbedded: false,
100+
})
101+
.then(() => {
102+
setStatus('ready');
103+
})
104+
.catch(() => {
105+
setStatus('error');
106+
})
107+
.finally(() => {
108+
inFlightRef.current = null;
109+
});
110+
111+
inFlightRef.current = request;
112+
return request;
113+
}, [routeData]);
114+
115+
const label = {
116+
idle: 'Prefetch payload',
117+
loading: 'Prefetching payload',
118+
ready: 'Prefetch completed',
119+
error: 'Prefetch failed',
120+
}[status];
121+
122+
return (
123+
<Button
124+
type="button"
125+
variant={status === 'error' ? 'destructive' : 'outline'}
126+
className="mt-1"
127+
disabled={!routeData || status === 'loading'}
128+
onClick={() => void prefetchPayload()}
129+
>
130+
{status === 'loading' ? <RefreshCw className="size-4 animate-spin" aria-hidden="true" /> : null}
131+
{label}
132+
</Button>
133+
);
134+
}
135+
86136
function ClientSignalPanel({ className }: { className?: string }) {
87137
const [pulses, setPulses] = useState(0);
88138

@@ -280,6 +330,7 @@ function ShowcasePage({ appProps, routeData }: { appProps: RscShowcaseAppProps;
280330
<RefreshCw className="size-4" aria-hidden="true" />
281331
Refetch payload
282332
</Button>
333+
<PayloadPrefetchButton key={routeData?.requestPath ?? 'unavailable'} routeData={routeData} />
283334
</CardContent>
284335
</Card>
285336
</header>

docs/01-architecture.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# Architecture
22

3-
This starter begins from `create-react-on-rails-app --rsc --rspack` and currently targets the React on Rails Pro `17.0.0-rc.3` RC stack with Shakapacker `10.1.0`.
3+
This starter begins from `create-react-on-rails-app --rsc --rspack` and currently targets the React on Rails Pro `17.0.0-rc.7` RC stack with React on Rails RSC `19.2.1-rc.0` and Shakapacker `10.2.0`.
44

5-
Shakapacker remains on `10.1.0` because public `11.1.0` artifacts are not visible in the registries this starter consumes. Rspack is the checked-in local and deploy default. `react-on-rails-rsc@19.0.5-rc.7` provides the Rspack RSC manifest support this starter needs, while Webpack remains an opt-in bridge/comparison path.
5+
Shakapacker remains on `10.2.0` because the React on Rails 17 RC7 upgrade did not require a coupled Shakapacker bump. Rspack is the checked-in local and deploy default. `react-on-rails-rsc@19.2.1-rc.0` provides the Rspack RSC manifest support this starter needs, while Webpack remains an opt-in bridge/comparison path.
66

77
## Related React On Rails Docs
88

@@ -104,7 +104,7 @@ Vite, file-based routing, Hotwire, or Stimulus.
104104
Rspack is the active bundler in `config/shakapacker.yml`. Development disables client lazy compilation at the config top level and at `experiments.lazyCompilation`, uses live reload by default for this RC stack, and gates TanStack devtools behind `localStorage["tanstack-devtools"] = "1"` to avoid dev-server overlay requests from optional chunks. Explicit HMR mode enables React Fast Refresh through Shakapacker's Rspack wiring, while static and production-assets dev modes remain free of Rspack dev-server clients.
105105

106106
The public React Server Components path is green on the local Rspack default
107-
with `react-on-rails-rsc@19.0.5-rc.7`. The Rspack client, server, and
107+
with `react-on-rails-rsc@19.2.1-rc.0`. The Rspack client, server, and
108108
server-only RSC bundles compile and emit the React client/server manifests
109109
expected by the React on Rails RSC client-reference path. The Webpack bridge
110110
remains documented in [RSC Webpack Bundler Spike](09-rsc-webpack-bundler-spike.md)

docs/02-vs-inertia.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ renderer such as React on Rails Pro. That is an architectural inference, not a
149149
claim about Inertia's roadmap.
150150

151151
The Rspack status is also intentionally conservative. Rspack remains the local
152-
and deploy default, and `react-on-rails-rsc@19.0.5-rc.7` emits the RSC
152+
and deploy default, and `react-on-rails-rsc@19.2.1-rc.0` emits the RSC
153153
client-reference manifests required by React on Rails Pro. The status is tracked
154154
in [SPIKE.md](../SPIKE.md) and [Tested Modes](06-tested-modes.md). The Webpack
155155
bridge remains an opt-in comparison path.

docs/06-tested-modes.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
# Tested Modes
22

33
This starter keeps the TanStack dashboard on Rspack and the launch RC stack:
4-
React on Rails Pro `17.0.0-rc.3` with Shakapacker `10.1.0`.
5-
Shakapacker is intentionally not documented as `11.1.0` because public
6-
`11.1.0` artifacts are not visible in the registries consumed by the starter.
4+
React on Rails Pro `17.0.0-rc.7`, React on Rails RSC `19.2.1-rc.0`, and
5+
Shakapacker `10.2.0`.
6+
Shakapacker remains on `10.2.0` because the React on Rails 17 RC7 upgrade did
7+
not require a coupled Shakapacker bump.
78
Rspack is the default local and deploy bundler in the checked-in matrix.
89
Webpack has explicit opt-in bridge/comparison smokes below.
910

@@ -61,7 +62,7 @@ build, rendering, or routing behavior.
6162
| HMR dev | `bin/test hmr` or `SHAKAPACKER_DEV_SERVER_HMR=true bin/dev --no-open-browser --route=dashboard` | CI | Boots the same default dev stack with `hmr: true`, `live_reload: false`, and React Fast Refresh enabled by Shakapacker's Rspack integration, then verifies the authenticated TanStack routes hydrate, navigate, and observe a browser-open source edit. This smoke does not assert state-preserving React Fast Refresh updates. |
6263
| Webpack HMR bridge | `bin/test webpack-hmr` or `SHAKAPACKER_ASSETS_BUNDLER=webpack node script/dev-mode-smoke.mjs hmr` | Webpack bridge sentinel | Boots Rails, Webpack dev-server, SolidQueue, the Node renderer, and the server/RSC bundle watchers with HMR enabled, then verifies the authenticated TanStack routes hydrate, navigate, and observe a browser-open source edit. This keeps the optional Webpack path covered without changing the Rspack default. |
6364
| Webpack HMR RSC bridge | `bin/test webpack-hmr-rsc` or `SHAKAPACKER_ASSETS_BUNDLER=webpack REQUIRE_RSC_MANIFESTS=true node script/dev-mode-smoke.mjs hmr hello-server` | Webpack RSC/HMR sentinel | Boots the Webpack HMR stack against `/hello_server`, waits for both RSC client-reference manifests, and fails if the route falls back before rendering the RSC page. This verifies the optional Webpack dev-server writes the client manifest where React on Rails Pro can consume it while still excluding hot-update artifacts from disk. |
64-
| Rspack/RSC client boundary repro | `bin/test rsc-repro` or `pnpm run repro:rspack-rsc` | CI status, upstream repro | Builds Rspack bundles, verifies the generated `HelloServer` RSC example still contains a `'use client'` boundary, and confirms both RSC client-reference manifests are emitted with `react-on-rails-rsc@19.0.5-rc.7`. Use `REQUIRE_RSC_MANIFESTS=true` when this focused check must fail hard on a manifest regression. |
65+
| Rspack/RSC client boundary repro | `bin/test rsc-repro` or `pnpm run repro:rspack-rsc` | CI status, upstream repro | Builds Rspack bundles, verifies the generated `HelloServer` RSC example still contains a `'use client'` boundary, and confirms both RSC client-reference manifests are emitted with `react-on-rails-rsc@19.2.1-rc.0`. Use `REQUIRE_RSC_MANIFESTS=true` when this focused check must fail hard on a manifest regression. |
6566
| `/hello_server` RSC route smoke | `bin/test hello-server-rsc` or `pnpm run test:hello-server-rsc` | RSC route sentinel | Boots the static-assets dev stack against `/hello_server` and verifies the route renders the demo shell with the RSC manifests available. Use `REQUIRE_RSC_MANIFESTS=true` when intentionally requiring interactive RSC client-reference manifests. |
6667
| Production precompile | `bin/test production-precompile` or `RAILS_ENV=production SECRET_KEY_BASE_DUMMY=1 REACT_ON_RAILS_STARTER_TANSTACK_DATABASE_PASSWORD=dummy bin/rails assets:precompile` | Release-impacting checks | Confirms production Rspack client, server, and RSC bundles compile. The expected Pro license warning is non-fatal. |
6768
| Production boot smoke | `bin/test production-boot` or `node script/production-boot-smoke.mjs` | CI, release-impacting checks | Precompiles production assets with dummy secrets, prepares production-mode databases, starts `client/node-renderer.js` from compiled output, boots Rails in `RAILS_ENV=production`, checks `/up`, signs in as `demo@example.com / password`, and checks `/projects?status=active&sort=name&dir=asc` with `X-Forwarded-Proto: https`. |
@@ -115,5 +116,5 @@ build, rendering, or routing behavior.
115116
with `experiments.lazyCompilation = false` kept explicit for compatibility.
116117
Otherwise dynamic TanStack devtools imports can route through Rspack
117118
lazy-trigger URLs that return 404s in development.
118-
- Historical Rspack/RSC manifest tracking lives in [shakacode/react_on_rails#1828](https://github.com/shakacode/react_on_rails/issues/1828). The starter requires `react-on-rails-rsc@19.0.5-rc.7` or newer for the Rspack manifest path.
119+
- Historical Rspack/RSC manifest tracking lives in [shakacode/react_on_rails#1828](https://github.com/shakacode/react_on_rails/issues/1828). The starter requires `react-on-rails-rsc@19.2.1-rc.0` or newer for the Rspack manifest path.
119120
- The React on Rails Pro TanStack Router private-store compatibility issue is tracked in [shakacode/react_on_rails#3375](https://github.com/shakacode/react_on_rails/issues/3375).

docs/08-why-rsc-on-rails.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ inside one Rails app.
226226
## Limitations And Current State
227227

228228
This starter intentionally keeps Rspack as the local and deploy default.
229-
`react-on-rails-rsc@19.0.5-rc.7` provides the Rspack plugin path that emits the
229+
`react-on-rails-rsc@19.2.1-rc.0` provides the Rspack plugin path that emits the
230230
RSC client-reference manifests required by React on Rails Pro. That status is
231231
tracked in [SPIKE.md](../SPIKE.md), and the small reproduction remains available
232232
through `pnpm run repro:rspack-rsc`.

0 commit comments

Comments
 (0)