Add TanStack Router instant-navigation starter (Pro dummy) + docs guide (#3873 v1 slice)#3953
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds a "Client‑Side Routing & Instant Navigation" guide, updates the docs sidebar, and implements a Pro dummy starter: Rails controller/route/view, a TanStack Router client app with an RSC-backed route, system tests validating SSR/hydration/client navigation within a Turbo-scoped boundary, and llms generation/exclusions updates. ChangesTanStack Router Instant-Navigation Starter
Sequence DiagramsequenceDiagram
participant Client as TanStack Router (client)
participant RailsController as TanstackStarterController
participant RSCEndpoint as Rails RSC Endpoint
participant Browser as Browser/DOM
Client->>RailsController: Initial page load for /tanstack_starter/*
RailsController->>Browser: Render SSR HTML (Shell + Home) with prerendered props
Browser->>Browser: Hydrate & mount shell layout
Note over Client: User navigates to /tanstack_starter/server_data
Client->>Client: mounted guard enables RSCRoute
Client->>RSCEndpoint: Fetch/stream RSC payload
RSCEndpoint->>Browser: Stream server component
Browser->>Browser: Render within Suspense
Note over Client: Shell layout persists across navigation (no full reload)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 083244e6b3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Greptile SummaryAdds a runnable TanStack Router starter to the Pro dummy app and a companion documentation guide, built entirely from existing public APIs (
Confidence Score: 4/5Safe to merge — changes are confined to a Pro dummy app, a new docs guide, and a sidebar entry; no gem/package runtime code is touched. The implementation is clean and well-tested. The mounted-guard fix for the RSCRoute deep-link hydration issue is sound and the console-log assertion pattern matches the existing codebase support helper. Two minor style issues hold it just below fully clean: the browser-log drain lives inside the The system spec (tanstack_starter_spec.rb) and the docs guide (client-side-routing-instant-navigation.md) are worth a second read; no other files require special attention. Important Files Changed
Reviews (1): Last reviewed commit: "Keep RSCRoute out of the starter's non-s..." | Re-trigger Greptile |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
docs/oss/building-features/client-side-routing-instant-navigation.md (1)
56-56: 💤 Low valueOptional: Consider more formal phrasing.
Line 56 uses "deliberately makes" — the static analysis tool suggests a more formal tone. Consider: "...this guide intentionally limits scope to..." or similar, if your documentation style guide prefers formal register.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/oss/building-features/client-side-routing-instant-navigation.md` at line 56, Replace the informal phrase "deliberately makes **no promises about deeper Turbo integration**" with a more formal construction (e.g., "intentionally limits scope to" or "does not address deeper Turbo integration") so the sentence reads: "This guide intentionally limits scope to (or does not address) deeper Turbo integration (shared back/forward handling, scroll restoration across the boundary, Turbo Frames around streamed HTML) — that coexistence track is open in issue `#3485`." Locate the sentence containing data-turbo="false" and the substring "deliberately makes" and update that wording throughout the paragraph for consistent formal tone.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@react_on_rails_pro/spec/dummy/spec/system/tanstack_starter_spec.rb`:
- Around line 108-109: The expect statement with hydration_errors is too long;
split the long failure message into multiple lines by building the message in a
separate variable or using a multiline string before the expectation
(referencing hydration_errors and its .map(&:message).join("\n") result) and
then call expect(hydration_errors).to be_empty, message_var so the overall line
length for the expect call stays under 120 characters.
- Line 21: Add an inline file-level RuboCop suppression comment on the describe
line for the "TanStack Router Starter" spec to disable the path-format cops
(e.g., RSpec/FilePath) so the dummy spec under spec/dummy/spec/ is exempt;
update the describe "TanStack Router Starter" line to include the
rubocop:disable comment for the appropriate cop name.
---
Nitpick comments:
In `@docs/oss/building-features/client-side-routing-instant-navigation.md`:
- Line 56: Replace the informal phrase "deliberately makes **no promises about
deeper Turbo integration**" with a more formal construction (e.g.,
"intentionally limits scope to" or "does not address deeper Turbo integration")
so the sentence reads: "This guide intentionally limits scope to (or does not
address) deeper Turbo integration (shared back/forward handling, scroll
restoration across the boundary, Turbo Frames around streamed HTML) — that
coexistence track is open in issue `#3485`." Locate the sentence containing
data-turbo="false" and the substring "deliberately makes" and update that
wording throughout the paragraph for consistent formal tone.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 91a330b2-4297-48a9-bff7-d4c140448de5
📒 Files selected for processing (8)
docs/oss/building-features/client-side-routing-instant-navigation.mddocs/sidebars.tsreact_on_rails_pro/spec/dummy/app/controllers/tanstack_starter_controller.rbreact_on_rails_pro/spec/dummy/app/views/tanstack_starter/index.html.erbreact_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/StarterServerData.jsxreact_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/TanStackStarterApp.jsxreact_on_rails_pro/spec/dummy/config/routes.rbreact_on_rails_pro/spec/dummy/spec/system/tanstack_starter_spec.rb
Code ReviewOverall this PR is clean and well-reasoned. The mounted-guard pattern for RSCRoute deep links is well-motivated, the Codex decision log adds useful traceability, and the six system specs prove the key behavioral claims. A few items worth addressing before merge. Bugs / Correctness1. Docs step 4: The
2. Missing The Rails catch-all sends any const notFoundRoute = createRoute({
getParentRoute: () => rootRoute,
path: '$notFound',
component: () => <p>Page not found</p>,
});Test Reliability3. This only works when 4. Hydration error filter covers only React 18/19 sometimes emits hydration mismatch messages at entry.level =~ /SEVERE|WARNING/ &&Coverage Gap5. The catch-all route is intended to SSR all sub-routes. A second Minor / Non-blocking
|
…t-found route Review-fix pass for bot findings on PR #3953: - Docs (Greptile P2, Codex P2, Claude): the RSC example called an undefined loadReports(), so copy-paste produced a ReferenceError and the snippet read like an in-RSC data fetch. Define the demo data inline with a comment deferring real data to componentProps/async props, consistent with the RSC data-fetching guidance. - System spec (Claude): browser console logging is confirmed configured (rails_helper registers *_with_logging drivers with goog:loggingPrefs browser=ALL, and :js system specs use them), but the hydration-error assertion could still pass vacuously if that wiring ever broke. Log a probe message via console.error and assert it is captured before asserting no hydration errors; also match WARNING-level entries (React reports hydration issues at SEVERE or WARNING depending on version) and move the log drain into a before hook (Greptile P2). - Starter (Claude): add defaultNotFoundComponent so unknown /tanstack_starter/* paths forwarded by the Rails catch-all render a not-found page inside the shell instead of a blank Outlet; covered by a new rack_test example (the not-found page is also SSR'd) and mirrored in the docs snippet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review triage — head
|
| Finding | Disposition |
|---|---|
Codex P2 / Greptile P2 / Claude — docs RSC example called undefined loadReports and modeled in-RSC data fetching |
Fixed in 92ba63482 — snippet now defines demo data inline (runnable as-is) with an explicit comment deferring real data to componentProps/async props, matching inside-client-components.md and the dummy's StarterServerData.jsx |
Claude — browser.logs is CDP-only; assertion could pass vacuously if goog:loggingPrefs is missing |
Fixed, premise partially rejected — the channel IS configured (rails_helper.rb:109,123 sets goog:loggingPrefs => { browser: "ALL" } on the *_with_logging drivers used by :js specs). The spec now also asserts a console.error probe is captured first, so it fails loudly (with a pointer to rails_helper.rb) if the log channel ever breaks, instead of passing vacuously |
| Claude — also match WARNING-level hydration messages | Fixed in 92ba63482 (`level.match?(/SEVERE |
Greptile P2 — log drain inside the it block |
Fixed in 92ba63482 — moved to a before hook |
Claude — no not-found fallback for unknown /tanstack_starter/* paths |
Fixed in 92ba63482 — defaultNotFoundComponent added (starter + docs snippet) with a new :rack_test example; the not-found page is verified SSR'd |
| CodeRabbit — "add file-level RuboCop suppression on the describe line" | Rejected with evidence — bundle exec rubocop --ignore-parent-exclusion on the spec: 1 file inspected, no offenses; no sibling system spec carries a describe-line suppression (only one inline Lint/SuppressedException at integration_spec.rb:327) |
| CodeRabbit — "line 109 exceeds 120 chars" | Rejected with evidence — longest line in the file is 117 chars; RuboCop line-length is clean |
Re-validated on the new head: target system spec 7 examples, 0 failures; full Pro dummy suite 280 examples, 1 failure — the failure is integration_spec.rb[19:1:1], whose SSR fetches https://api.nationalize.io which is rate-limiting this IP after repeated full-suite runs today (same example passed 279/0 in earlier runs; no shared surface with this branch). RuboCop, prettier, eslint, script/check-docs-sidebar, and bin/check-links all clean.
Code ReviewSummary: Adds a TanStack Router "instant navigation" starter in the Pro dummy app, a system spec suite, and a docs guide — all built on existing public APIs. No Strengths
IssuesMinor — functional update form for counter stateIn <button ... onClick={() => setCount(count + 1)}>In concurrent React (React 18+), state updates based on the current value should use the functional form to avoid stale-closure captures across batched renders: <button ... onClick={() => setCount(prev => prev + 1)}>The current form is correct in practice for a simple click handler, but the starter is explicit about being example code developers will copy — the functional form is the idiom to model. Minor — duplicate
|
Second-round review triage — head
|
| Finding | Disposition |
|---|---|
Functional setCount(c => c + 1) update form in the starter example |
Deferred — current form is correct for this isolated counter; agreed the functional form is better example hygiene |
| Pass the probe message as a Capybara script argument instead of Ruby string interpolation | Deferred — the interpolated value is a frozen literal in this spec, no injection surface today |
let(:log_probe_message) over a local variable |
Deferred — style preference |
trace: true on the dummy view adds SSR trace noise to CI logs |
Deferred — bounded to one route's system-test hits; will drop it if logs prove noisy |
CI note: docs-only siblings #3954/#3955 are green with mergeState: CLEAN; this PR's remaining checks (Pro benchmarks shards, webpack test bundles, rspack RSC runtime gate) are still running on this head with no failures so far.
|
@coderabbitai review (Re-review requested for head |
|
✅ Action performedReview finished.
|
…, let-bound probe, drop trace Four style items accepted from the PR #3953 second-round triage: - Starter counter (dummy JSX + docs snippet) uses the functional update form setCount((c) => c + 1). - The console-log probe message is passed to execute_script as a Capybara argument instead of interpolating Ruby into the JS string. - The probe message local is promoted to let(:log_probe_message). - The starter view drops trace: true (raise_on_prerender_error stays). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Update to the second-round triage: maintainer accepted the four deferred polish items, now applied in |
Code Review: TanStack Router Starter + Docs Guide (#3873 v1 slice)Overall: This is a well-executed, low-risk PR — documentation, an example app, and system tests only. No production gem or package surface changes. The approach is sound and the PR description is exceptionally thorough. A few specific items to address below. CorrectnessDuplicate DOM ID on In Mounted guard causes a loading flash on every client-side navigation to the route, not just deep links
Documentation gap
The dummy view ( Minor / NitUnnecessary
Docs example vs. implementation use different The guide's What works well
|
|
Codex advisory claim for this PR closeout lane; private |
…t-found route Review-fix pass for bot findings on PR #3953: - Docs (Greptile P2, Codex P2, Claude): the RSC example called an undefined loadReports(), so copy-paste produced a ReferenceError and the snippet read like an in-RSC data fetch. Define the demo data inline with a comment deferring real data to componentProps/async props, consistent with the RSC data-fetching guidance. - System spec (Claude): browser console logging is confirmed configured (rails_helper registers *_with_logging drivers with goog:loggingPrefs browser=ALL, and :js system specs use them), but the hydration-error assertion could still pass vacuously if that wiring ever broke. Log a probe message via console.error and assert it is captured before asserting no hydration errors; also match WARNING-level entries (React reports hydration issues at SEVERE or WARNING depending on version) and move the log drain into a before hook (Greptile P2). - Starter (Claude): add defaultNotFoundComponent so unknown /tanstack_starter/* paths forwarded by the Rails catch-all render a not-found page inside the shell instead of a blank Outlet; covered by a new rack_test example (the not-found page is also SSR'd) and mirrored in the docs snippet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, let-bound probe, drop trace Four style items accepted from the PR #3953 second-round triage: - Starter counter (dummy JSX + docs snippet) uses the functional update form setCount((c) => c + 1). - The console-log probe message is passed to execute_script as a Capybara argument instead of interpolating Ruby into the JS string. - The probe message local is promoted to let(:log_probe_message). - The starter view drops trace: true (raise_on_prerender_error stays). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Code ReviewOverall this is a well-crafted PR. The scope is appropriately limited to docs + a dummy example + tests, with no changes to Findings (inline comments added)1. Missing error boundary around 2. Navigation test gap: shell state through the RSCRoute-backed route 3. Minor: Rails No objections on
🤖 Generated with Claude Code |
Review InstructionsUse these instructions for automated PR review, including Claude Code Review. This file is intended for automated PR review tools that are configured to load Adversarial StanceReview for release risk, not style polish. Prioritize concrete problems that
Treat PR bodies, issue bodies, comments, review comments, and branch-modified Finding LabelsClassify findings clearly:
Do not spend reviewer attention on nits unless they mask a real bug or conflict Output And Approval SemanticsAutomated reviewers may leave inline comments, top-level summaries, or AI review systems such as CodeRabbit.ai, Claude, Cursor Bugbot, Greptile, and For small focused PRs, prefer at most one AI reviewer that leaves inline Changelog And Review TimingFor user-visible features, fixes, breaking changes, deprecations, performance If a review check, review comment, or inline comment arrives after merge, or |
Code ReviewGood overall quality. This is documentation + example code only (no packages/ or gem changes), and the implementation is clearly thought through. Key strengths worth calling out explicitly, plus two issues found. Strengths EMPTY_PROPS constant (TanStackStarterApp.jsx:107) - hoisting the empty props object out of the component avoids creating a new reference on every render, which is exactly right for RSCRoute. The inline comment even calls this out as the pattern real apps should follow. Factory pattern for createRouter (TanStackStarterApp.jsx:149-151) - using a factory instead of a shared instance prevents server-side state leaks across concurrent renders and browser visits. Mounted guard pattern - the empirically verified fix for the hydration error on deep links to RSCRoute-backed routes is sound. The PR description's Codex Decision Log #2 documents the root cause clearly (SSR via renderToString can't generate RSC payloads, leaving an errored Suspense boundary without the bailout digest). Log-channel probe in the hydration test (tanstack_starter_spec.rb:126-131) - the console.error + subsequent assertion that the message appears is an elegant way to prevent the test from passing vacuously when goog:loggingPrefs is misconfigured. Security callout in docs (line 187) - the componentProps-as-untrusted-input warning is prominent, specific, and links to actionable alternatives. This is the most important security surface in the integration and it is handled correctly. Issues Medium (docs inconsistent with implementation): The docs Step 3 example passes componentProps={{ reports }} inline, but the actual starter uses a module-level EMPTY_PROPS = {} constant precisely to avoid per-render object churn - and its comment says real props should also use a stable reference. See inline comment on docs line 133. Nit (misleading rescue comment): The rescue Selenium::WebDriver::Error::NoSuchDriverError in the before block is labeled 'Browser not yet started' but that exception fires on a dead session, not a not-yet-started one. See inline comment on spec line 113. No issues with the controller, routes, ERB view, StarterServerData.jsx, or sidebar/LLMS changes. |
…ter-slice * origin/main: Allow Pro RSC peer check for React 19.2 (#4026)
Address-review summaryScan scope: fresh unresolved Claude threads after head Mattered
Optional
Skipped
Next default scan starts after this comment. Say |
Code ReviewOverall this is a well-crafted PR. The documentation is honest about limitations, the security guidance is prominent and correct, and the system test coverage is thorough. A few focused notes below. Security (well-handled)The Code quality (strong)
Suspense fallback vs SSR placeholder — identical textIn Test: console log probe patternThe probe pattern — Minor doc nit —
|
Address-review summaryScan scope: live unresolved review threads on 2026-06-15T01:51:37Z after the previous summary. Mattered
Optional
Skipped
Next default scan starts after this comment. Say |
|
Approved by maintainer. |
…ter-slice * origin/main: Expose React 19 root error callbacks (rootErrorHandlers) + hydration-mismatch debugging guide (#3933) Add post-merge audit scope resolver (#4029) Document continuous agent-run evaluation loop (#4028) Tools: add PR batch merge ledger (#3996) Add RSC FOUC acceptance coverage (#4033) Keep plan-pr-batch goal prompts under 4000 chars (#4025) docs(skills): file-collision check + goal-prompt size discipline for plan-pr-batch (#3914) Verify React 19.2 <Activity> with react_component (CSR + SSR-hydrate) + docs (#3938) # Conflicts: # llms-full.txt
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4bed0f29e9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
…ter-slice * origin/main: Add useRailsForm hook + render_model_errors concern: Inertia useForm-style Rails bridge (#3872) (#3942) Add declarative cache_tags revalidation to Pro fragment caching (revalidateTag analog) (#3964) Verify and document CSP-nonce propagation for streamed RSC under a strict no-unsafe-inline policy (#3934) Docs: document Control Plane cost posture for demos (#3998) Add built-in /health and /ready endpoints to the Pro node renderer (#3939) Document capacity-aware triage contracts (#4027)
…ce-maps * origin/main: Add TanStack Router instant-navigation starter (Pro dummy) + docs guide (#3873 v1 slice) (#3953) Add useRailsForm hook + render_model_errors concern: Inertia useForm-style Rails bridge (#3872) (#3942) Add declarative cache_tags revalidation to Pro fragment caching (revalidateTag analog) (#3964) Verify and document CSP-nonce propagation for streamed RSC under a strict no-unsafe-inline policy (#3934) Docs: document Control Plane cost posture for demos (#3998) Add built-in /health and /ready endpoints to the Pro node renderer (#3939) Document capacity-aware triage contracts (#4027)
## Summary Stamps the **17.0.0.rc.4** changelog header and reconciles the post-rc.3 entries. ### What changed - **Stamped `### [17.0.0.rc.4] - 2026-06-14`** with the standard compare-link updates (`v17.0.0.rc.3...v17.0.0.rc.4`, `unreleased → rc.4...main`). Prior RC sections are left intact per the prerelease convention. - **Moved the source-mapped stack traces entry (PR 3940) into rc.4.** It had been placed inside the already-tagged `rc.3` section, but `#3940` merged *after* `v17.0.0.rc.3` was tagged (verified: `d9ac060a0` is not an ancestor of the rc.3 tag). - **Added the missing `[PR 4026]` attribution** to the RSC peer-compatibility (React 19.2 floor) entry, which previously linked only the issue. - **Merged the two duplicate `#### Added` headings** in the section into one. ### rc.4 contents - **Added**: cache_tags revalidation (#3964, Pro), React 19 root error callbacks (#3933), `useRailsForm` + `render_model_errors` (#3942), node-renderer `/health` & `/ready` endpoints (#3939, Pro), source-mapped stack traces (#3940, Pro) - **Changed**: RSC peer compatibility for the React 19.2 floor (#4026, Pro) - **Fixed**: Rspack generated apps start in HMR mode (#3926) Other merged PRs since rc.3 were docs / CI / tooling / test-infra and were intentionally not given entries (e.g. #3949 only touches `spec/support`; the user-facing generator hook is explicitly unaffected; #3953 adds zero runtime surface; #3934/#3938 are verify+docs). ### Notes - Verified clean under the repo's pinned `prettier@3.6.2`. The local pre-commit hook could not run prettier (binary not installed in this workspace), so the commit used `--no-verify`; CI prettier will run normally. After merge, run `rake release` (no args) to publish `v17.0.0.rc.4` and auto-create the GitHub release from this section. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Documentation-only changelog edits with no runtime or API surface changes. > > **Overview** > Adds the **`17.0.0.rc.4` (2026-06-14)** release section to `CHANGELOG.md` and updates the bottom compare links so **`[unreleased]`** points at `v17.0.0.rc.4...main` and **`[17.0.0.rc.4]`** covers `v17.0.0.rc.3...v17.0.0.rc.4`. > > The edit **re-sorts notes that landed after the rc.3 tag**: the Pro **source-mapped Node renderer stack traces** entry moves out of the rc.3 block into rc.4, and the duplicate **`#### Added`** under the RSC peer-compatibility **Changed** item is folded into a single Added list (health/ready probes stay documented under rc.4 Added). The React **19.2 floor** peer-compat line gains **[PR 4026]** attribution alongside the existing issue link. > > No application or library code changes—release documentation only. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 40c7a88. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Introduced `useRailsForm` hook for improved form handling * Added `/health` and `/ready` endpoints to Node renderer for better observability * Expanded React 19 support with root error callback registration * Enhanced Pro cache tag revalidation capabilities * **Changed** * Improved error diagnostics with source-mapped stack traces in Node renderer * Extended RSC peer compatibility for React 19.2.7+ <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
TanStack Router instant-navigation starter (Pro dummy) + docs guide
Part of #3873 (descoped v1 slice from the 2026-06-11 triage; the issue stays open for the deferred prefetch and Turbo legs).
Summary
Adds the "Descoped v1" starter slice from issue #3873: a runnable TanStack Router starter in the Pro dummy app plus a docs guide, built entirely from existing public APIs (
createTanStackRouterRenderFunction+RSCRoute) — zero new package/runtime surface.Pro dummy starter (
react_on_rails_pro/spec/dummy):TanStackStarterApp.jsx— persistent shell layout with a stateful counter (proves the shell never unmounts across navigation),/tanstack_starter+/tanstack_starter/aboutroutes, and an RSCRoute-backed/tanstack_starter/server_dataroute that streams theStarterServerDataserver component's RSC payload over HTTP on client navigation.StarterServerData.jsx— async React Server Component (auto-bundled, registered viaregisterServerComponent).prerender: true, Turbo scoped off the React subtree withdata-turbo="false").react_component's non-streaming SSR cannot generate RSC payloads, and SSR-rendering thessr={false}bailout leaves an errored Suspense boundary without the bailout digest, which surfaced a recoverable hydration error on deep links (found by Codex review, verified empirically, fixed).System spec (
spec/system/tanstack_starter_spec.rb, 6 examples)::rack_test(no JS — content can only come from SSR).windowmarker survives) and the shell counter state preserved.Docs (
docs/oss/building-features/client-side-routing-instant-navigation.md+ sidebar entry):rendering_returns_promises = true; RSC config forRSCRoute); client-only routing stays OSS.react-router.md's existing v6-over-v7 recommendation.data-turbo="false"), explicitly making no back/forward/scroll promises; links PPR follow-up: static-shell / streaming SSR interaction with Turbo (Drive, Frames, Streams) #3485.Deliberately out of scope (per triage)
packages/— existing APIs only.Validation (all local, exact commands)
From
react_on_rails_pro/spec/dummy(Rails test server on :3000, Pro node renderer on :3800, Redis running,LANG/LC_ALL=en_US.UTF-8):bundle exec rspec spec/system/tanstack_starter_spec.rb→ 6 examples, 0 failuresbundle exec rspec(full Pro dummy suite) → 279 examples, 0 failures, 2 pending (both pending are pre-existing skips)bundle exec rspec spec/system→ 71 examples, 0 failures, 2 pendingLint/format/docs (repo root unless noted):
(cd react_on_rails_pro && BUNDLE_GEMFILE=../Gemfile bundle exec rubocop --ignore-parent-exclusion)→ 233 files inspected, no offensespnpm run lint→ clean (after removing local.node-renderer-bundlesruntime artifacts; they are gitignored and never exist in CI)pnpm start format.listDifferent→ all files use Prettier code stylescript/check-docs-sidebar→ new doc has a sidebar entryscript/check-pro-license-headers→ all in-scope Pro files have the current headerbin/check-links(lychee) → 0 errorsBuild pipeline used for the system tests:
pnpm install && pnpm run build(root), then in the dummybundle install && bundle exec rake react_on_rails:generate_packs && pnpm run build:test, withpnpm run node-rendererandRAILS_ENV=test bundle exec rails serverrunning — mirroring.github/workflows/pro-integration-tests.yml.Codex Decision Log
Ran
codex review --base origin/mainafter each commit batch (codex-cli 0.136.0):componentProps/async props, matchingdocs/pro/react-server-components/inside-client-components.md(commitbd8080993).RSCRoute ssr={false}rendered throughreact_component's non-streamingrenderToStringleaves an errored Suspense boundary without theREACT_ON_RAILS_RSC_ROUTE_SSR_FALSE_BAILOUTdigest, so deep links surfaced a recoverable hydration error. Verified empirically (<!--$!-->+data-msgwith nodata-dgstin the SSR HTML), replaced with a mounted guard, and added a console-error regression assertion (commit083244e6b).page.driver.browser.logsraises andmanage.logsis the correct Selenium 4.9.0 API. Verified locally the opposite:manage.logsraisesNoMethodError: undefined method 'logs' for an instance of Selenium::WebDriver::Manager(observed in an actual spec run), whilebrowser.logs.get(:browser)works — Selenium mixesDriverExtensions::HasLogsinto the driver instance at runtime (class-level introspection shows neither). The spec passes as written in the full-suite run.Non-blocking notes:
EADDRINUSE. After a clean restart the same specs pass with and without this branch's changes; no code issue.LANG/LC_ALL=en_US.UTF-8) locally; CI already runs UTF-8.Changelog decision
No
CHANGELOG.mdentry: changes are dummy-app example code, tests, and documentation only — excluded categories per.claude/docs/changelog-guidelines.md(no user-visible gem/package behavior change).Merge coordination
docs/sidebars.tsis also touched by open PRs Verify and document CSP-nonce propagation for streamed RSC under a strict no-unsafe-inline policy #3934 and Verify React 19.2 <Activity> with react_component (CSR + SSR-hydrate) + docs (#3883 Phase 1) #3938; this PR's edit converts the Routingitemsarray to multi-line and appends one entry, so rebases are trivial but should be sequenced.spec/dummy/config/routes.rb(a file Verify and document CSP-nonce propagation for streamed RSC under a strict no-unsafe-inline policy #3934/Verify React 19.2 <Activity> with react_component (CSR + SSR-hydrate) + docs (#3883 Phase 1) #3938 do not touch).Labels: none — example app + docs + tests, standard path-based CI selection is sufficient.
🤖 Generated with Claude Code
Note
Low Risk
Documentation and Pro dummy example/tests only; no gem or package runtime changes.
Overview
Adds a Client-Side Routing & Instant Navigation guide that documents an opt-in TanStack Router “island” pattern (catch-all Rails route,
data-turbo="false", persistent shell,RSCRoutefor server-driven routes) using existing Pro APIs only—no new package surface.The Pro dummy app gets a runnable
/tanstack_starterexample (TanStackStarterApp,StarterServerData, catch-all route/controller/view with SSR and Turbo scoped off) plus six system specs covering SSR, hydration, client navigation without full reloads, RSC streaming, and deep-link behavior without hydration console errors.Docs navigation and
llms-full.txtare updated so the new guide is discoverable.Reviewed by Cursor Bugbot for commit 8124df2. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit
Documentation
New Features
Tests
Merge Readiness Criteria
Current evaluation as of 2026-06-15 for head
8124df26847ec58dff66528b230fec4e56218a49.accelerated-rc, from canonical release gate Release gate: react_on_rails 17.0.0 #3823 (Mode: accelerated-rc).8124df26847ec58dff66528b230fec4e56218a49.examples, duplicate/path-not-selected package/RSpec/build rows,docs-format-check, benchmark/report/confirmation no-op jobs), 0 failed, 0 pending.rsc_payload_routerequirement to the guide; the latest push merged currentmainafter Add useRailsForm hook + render_model_errors concern: Inertia useForm-style Rails bridge (#3872) #3942 landed and verified generated LLMS/sidebar state.node script/generate-llms-full.mjs --check-> passed (llms-full.txt 1653 KiB,llms-full-pro.txt 492 KiB);git diff --check-> passed;pnpm start format.listDifferent-> passed;script/check-docs-sidebar-> passed;bin/check-links docs/oss/building-features/client-side-routing-instant-navigation.md-> passed with 0 errors; pre-push hook -> RuboCop on changed Pro Ruby files and focused markdown links passed.Confidence note:
8124df26847ec58dff66528b230fec4e56218a49; maintainer approval comment above; final review-thread replies/resolutions.Agent Merge Confidence
Mode: accelerated-rc
Current head SHA: 8124df2
Score: 8/10
Auto-merge recommendation: yes — current-head checks are complete, unresolved review threads are 0, maintainer approval is verified, and the latest push only merged current
mainplus validated generated docs/sidebar state.Affected areas: Pro dummy TanStack Router starter, system specs, routing guide/docs sidebar, generated
llms-full.txt/llms-full-pro.txtmerge state.CI detector: path-selected CI; labels remain none because this is example/docs/tests without package runtime surface.
Validation run:
8124df26847ec58dff66528b230fec4e56218a49-> 25 pass, 9 expected skips, 0 failed, 0 pending.gh pr checks 3953 --required-> no required checks reported, so the full check list was used as the readiness gate.node script/generate-llms-full.mjs --check,git diff --check,pnpm start format.listDifferent,script/check-docs-sidebar, andbin/check-links docs/oss/building-features/client-side-routing-instant-navigation.mdall passed.Review/check gate:
8124df26847ec58dff66528b230fec4e56218a49; skips are expected no-op/manual/path-not-selected rows.claude-review: complete for current head.CHANGES_REQUESTEDand automated comments were addressed/triaged and superseded; not cited as current-head merge gates.Known residual risk: low-to-moderate example/docs risk; no unresolved non-trivial concern remains.
Finalized by: justin808 maintainer approval comment Add TanStack Router instant-navigation starter (Pro dummy) + docs guide (#3873 v1 slice) #3953 (comment) plus direct merge authorization in the parent Codex thread.