Skip to content

Add TanStack Router instant-navigation starter (Pro dummy) + docs guide (#3873 v1 slice)#3953

Merged
justin808 merged 19 commits into
mainfrom
jg/3873-tanstack-starter-slice
Jun 15, 2026
Merged

Add TanStack Router instant-navigation starter (Pro dummy) + docs guide (#3873 v1 slice)#3953
justin808 merged 19 commits into
mainfrom
jg/3873-tanstack-starter-slice

Conversation

@justin808

@justin808 justin808 commented Jun 12, 2026

Copy link
Copy Markdown
Member

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/about routes, and an RSCRoute-backed /tanstack_starter/server_data route that streams the StarterServerData server component's RSC payload over HTTP on client navigation.
  • StarterServerData.jsx — async React Server Component (auto-bundled, registered via registerServerComponent).
  • New catch-all Rails route + controller + view (prerender: true, Turbo scoped off the React subtree with data-turbo="false").
  • The RSC route is client-resolved via a mounted guard: react_component's non-streaming SSR cannot generate RSC payloads, and SSR-rendering the ssr={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):

  • Initial SSR of the route HTML asserted with :rack_test (no JS — content can only come from SSR).
  • Hydration proven by interactivity (counter click).
  • Client navigation between routes with no full page load (window marker survives) and the shell counter state preserved.
  • RSCRoute-backed route streams the server component over HTTP during client navigation.
  • Direct deep link to the RSC route resolves on the client with no hydration errors in the browser console (console log assertion).

Docs (docs/oss/building-features/client-side-routing-instant-navigation.md + sidebar entry):

  • Opt-in framing: a React-routed island inside a normal Rails app, coexisting with Rails routes — no view-layer takeover (vs Inertia) and no whole-app ownership (vs Next).
  • Pro requirements table (Node Renderer + rendering_returns_promises = true; RSC config for RSCRoute); client-only routing stays OSS.
  • Router-choice rationale: TanStack Router is the blessed starter router (first-class Pro SSR helper, typed routes); React Router stays the documented manual alternative, citing react-router.md's existing v6-over-v7 recommendation.
  • Persistent shell layout, RSCRoute streamed route (with the canonical Rails-owned-data-via-props guidance), and honest comparison framing vs Next App Router and Inertia, including current gaps.
  • Turbo: scoped OFF the React subtree (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)

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.rb6 examples, 0 failures
  • bundle 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 pending

Lint/format/docs (repo root unless noted):

  • (cd react_on_rails_pro && BUNDLE_GEMFILE=../Gemfile bundle exec rubocop --ignore-parent-exclusion) → 233 files inspected, no offenses
  • pnpm run lint → clean (after removing local .node-renderer-bundles runtime artifacts; they are gitignored and never exist in CI)
  • pnpm start format.listDifferent → all files use Prettier code style
  • script/check-docs-sidebar → new doc has a sidebar entry
  • script/check-pro-license-headers → all in-scope Pro files have the current header
  • bin/check-links (lychee) → 0 errors

Build pipeline used for the system tests: pnpm install && pnpm run build (root), then in the dummy bundle install && bundle exec rake react_on_rails:generate_packs && pnpm run build:test, with pnpm run node-renderer and RAILS_ENV=test bundle exec rails server running — mirroring .github/workflows/pro-integration-tests.yml.

Codex Decision Log

Ran codex review --base origin/main after each commit batch (codex-cli 0.136.0):

  1. P2 (accepted, fixed): docs example modeled fetching data from inside the RSC; the Pro Node renderer has no Rails models/session, so in-component fetches bypass Rails authorization/caching. Reworked the example + dummy comment to pass Rails-owned data via componentProps/async props, matching docs/pro/react-server-components/inside-client-components.md (commit bd8080993).
  2. P2 (accepted, fixed): RSCRoute ssr={false} rendered through react_component's non-streaming renderToString leaves an errored Suspense boundary without the REACT_ON_RAILS_RSC_ROUTE_SSR_FALSE_BAILOUT digest, so deep links surfaced a recoverable hydration error. Verified empirically (<!--$!--> + data-msg with no data-dgst in the SSR HTML), replaced with a mounted guard, and added a console-error regression assertion (commit 083244e6b).
  3. P1 (rejected with evidence): final pass claimed page.driver.browser.logs raises and manage.logs is the correct Selenium 4.9.0 API. Verified locally the opposite: manage.logs raises NoMethodError: undefined method 'logs' for an instance of Selenium::WebDriver::Manager (observed in an actual spec run), while browser.logs.get(:browser) works — Selenium mixes DriverExtensions::HasLogs into the driver instance at runtime (class-level introspection shows neither). The spec passes as written in the full-suite run.

Non-blocking notes:

  • A mid-session full-suite failure (streamed-component specs) was diagnosed as a local-environment artifact: bundles were rebuilt under a live Rails server whose digest cache went stale, and a first restart attempt silently failed with EADDRINUSE. After a clean restart the same specs pass with and without this branch's changes; no code issue.
  • The dummy system specs require a UTF-8 locale (LANG/LC_ALL=en_US.UTF-8) locally; CI already runs UTF-8.

Changelog decision

No CHANGELOG.md entry: 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

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, RSCRoute for server-driven routes) using existing Pro APIs only—no new package surface.

The Pro dummy app gets a runnable /tanstack_starter example (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.txt are 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

    • Added a comprehensive guide on client-side routing & instant navigation for embedding an SPA-like React router inside a Rails app, with examples, comparisons, known gaps, and sidebar entry; updated docs indexing/exclusions accordingly.
  • New Features

    • Added a runnable TanStack Router starter demonstrating SSR + hydration, persistent shell layout, client transitions, and React Server Component integration.
  • Tests

    • Added system tests validating SSR, hydration, in-shell navigation, RSCRoute data loading, and deep-link behavior without full page reloads.

Merge Readiness Criteria

Current evaluation as of 2026-06-15 for head 8124df26847ec58dff66528b230fec4e56218a49.

  • Release mode: accelerated-rc, from canonical release gate Release gate: react_on_rails 17.0.0 #3823 (Mode: accelerated-rc).
  • Current head SHA: 8124df26847ec58dff66528b230fec4e56218a49.
  • CI/check status: Complete for current head. No required checks are reported for this branch, so the full check list is the gate: 25 passing, 9 expected skips (examples, duplicate/path-not-selected package/RSpec/build rows, docs-format-check, benchmark/report/confirmation no-op jobs), 0 failed, 0 pending.
  • Review-thread status: GraphQL sweep immediately before readiness refresh: unresolved review threads = 0.
  • Review feedback triage: Prior Claude/Codex/Cursor/CodeRabbit feedback has been fixed or explicitly triaged in the decision log and review-thread replies. The final review fix added the missing rsc_payload_route requirement to the guide; the latest push merged current main after Add useRailsForm hook + render_model_errors concern: Inertia useForm-style Rails bridge (#3872) #3942 landed and verified generated LLMS/sidebar state.
  • Validation run: 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.
  • Label/CI decision: Labels: none. Scoped Pro dummy example + docs/tests change uses path-selected CI; current-head full check list is complete.
  • Known residual risk: Low-to-moderate example/docs risk. Pro dummy system validation recorded in the PR body remains applicable; the latest merge update only integrated already-merged main changes and regenerated/validated docs references.
  • Merge recommendation: Merge. Maintainer approval verified via Add TanStack Router instant-navigation starter (Pro dummy) + docs guide (#3873 v1 slice) #3953 (comment).

Confidence note:

  • Validated: current-head full GitHub checks, 0 unresolved review threads, LLMS check, whitespace, Prettier, docs sidebar, focused link check, pre-push RuboCop/link hooks, and prior Pro dummy system validation recorded above.
  • Evidence: GitHub checks for 8124df26847ec58dff66528b230fec4e56218a49; maintainer approval comment above; final review-thread replies/resolutions.
  • UNKNOWN: none for the merge gate.
  • Residual risk: low-to-moderate example/docs risk; no runtime package API changed.
  • Decision points: 1 (maintainer approval comment on 2026-06-15).

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 main plus 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.txt merge state.
CI detector: path-selected CI; labels remain none because this is example/docs/tests without package runtime surface.
Validation run:

  • GitHub checks for 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.
  • GraphQL review-thread sweep -> unresolved count 0.
  • Local validation for final head: node script/generate-llms-full.mjs --check, git diff --check, pnpm start format.listDifferent, script/check-docs-sidebar, and bin/check-links docs/oss/building-features/client-side-routing-instant-navigation.md all passed.
    Review/check gate:
  • GitHub checks: complete for 8124df26847ec58dff66528b230fec4e56218a49; skips are expected no-op/manual/path-not-selected rows.
  • Review threads: GraphQL unresolved count is 0.
  • Current-head reviewer/check verdicts:
    • claude-review: complete for current head.
    • Cursor Bugbot: complete for current head.
    • CodeRabbit: complete for current head.
    • CodeQL and docs/package/Ruby/JS/dummy/Pro checks: complete for current head.
  • Stale reviewer verdicts, advisory only: older CodeRabbit CHANGES_REQUESTED and 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.

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds 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.

Changes

TanStack Router Instant-Navigation Starter

Layer / File(s) Summary
Documentation guide and sidebar integration
docs/oss/building-features/client-side-routing-instant-navigation.md, docs/sidebars.ts
New guide documenting the opt-in Rails + TanStack Router + RSCRoute pattern with SSR+hydration examples and sidebar entry added under Routing.
Rails backend infrastructure
react_on_rails_pro/spec/dummy/app/controllers/tanstack_starter_controller.rb, react_on_rails_pro/spec/dummy/config/routes.rb, react_on_rails_pro/spec/dummy/app/views/tanstack_starter/index.html.erb
Adds catch-all route tanstack_starter(/*all), controller index action setting @starter_props, and ERB view mounting TanStackStarterApp inside a data-turbo="false" boundary with prerender.
Client-side TanStack Router app and RSC component
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/TanStackStarterApp.jsx, react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/StarterServerData.jsx
Exports a TanStack router render function with a persistent ShellLayout, routes for home/about/server_data, a mounted guard that conditionally renders RSCRoute inside Suspense, and StarterServerData as a React Server Component providing server-streamed content.
System tests validating SSR, hydration, and navigation
react_on_rails_pro/spec/dummy/spec/system/tanstack_starter_spec.rb
System specs validate server-side shell rendering, data-turbo="false" scoping, client-side navigation without reloads preserving shell state, RSCRoute streaming on client navigation, and direct deep-link hydration checks.
LLMS/full content and exclusions
docs/.llms-exclusions, llms-full.txt
Adds the new guide content to llms-full.txt, removes listed misc docs from the stream, and updates docs/.llms-exclusions to keep misc pages out of generation.

Sequence Diagram

sequenceDiagram
  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)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

enhancement, review-needed, documentation, P2

Suggested reviewers

  • AbanoubGhadban
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add TanStack Router instant-navigation starter (Pro dummy) + docs guide (#3873 v1 slice)' accurately summarizes the main changes: adding a TanStack Router starter to the Pro dummy app and accompanying documentation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jg/3873-tanstack-starter-slice

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread docs/oss/building-features/client-side-routing-instant-navigation.md Outdated
@greptile-apps

greptile-apps Bot commented Jun 12, 2026

Copy link
Copy Markdown

Greptile Summary

Adds a runnable TanStack Router starter to the Pro dummy app and a companion documentation guide, built entirely from existing public APIs (createTanStackRouterRenderFunction + RSCRoute). No package or runtime changes are included.

  • Pro dummy starter (TanStackStarterApp.jsx, StarterServerData.jsx, controller, view, route): demonstrates a persistent shell layout, client-side navigation, and an RSCRoute-backed route that streams an RSC payload; uses a mounted guard to sidestep the SSR hydration issue on deep links to RSCRoute pages.
  • System spec (6 examples): covers SSR via rack_test, hydration interactivity, no-reload navigation with shell state preservation, RSC payload streaming on client navigation, and a browser-console assertion that deep links produce no recoverable hydration errors.
  • Docs guide (client-side-routing-instant-navigation.md + sidebar entry): explains the opt-in island pattern, requirements table, router-choice rationale, step-by-step setup, and an honest comparison with Next.js App Router and Inertia including current gaps.

Confidence Score: 4/5

Safe 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 it block rather than a before hook, and a docs snippet uses an undefined loadReports pseudo-function that a reader could copy verbatim and hit a ReferenceError.

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

Filename Overview
react_on_rails_pro/spec/dummy/spec/system/tanstack_starter_spec.rb New system spec (6 examples) covering SSR, hydration, client navigation, RSC streaming, and deep-link console-error assertion; the log-drain pattern matches the existing codebase support helper.
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/TanStackStarterApp.jsx Clean TanStack Router starter with a persistent shell layout, three route pages, and a mounted-guard pattern for RSCRoute to avoid SSR hydration conflicts.
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/StarterServerData.jsx Minimal async RSC returning hardcoded demo data; suitable as a dummy-app example with an accurate comment directing real apps to use componentProps instead of in-component fetches.
react_on_rails_pro/spec/dummy/app/controllers/tanstack_starter_controller.rb Single-action controller setting empty props; straightforward catch-all handler for the React-routed subtree.
react_on_rails_pro/spec/dummy/app/views/tanstack_starter/index.html.erb View wraps the React component in a data-turbo="false" boundary and passes prerender: true and raise_on_prerender_error: true; correct SSR/Turbo scoping.
react_on_rails_pro/spec/dummy/config/routes.rb Single-line addition of the tanstack_starter(/*all) catch-all route; correctly placed and named.
docs/oss/building-features/client-side-routing-instant-navigation.md New 188-line guide covering the opt-in island pattern, TanStack Router rationale, step-by-step starter, honest gap disclosure, and comparison with Next.js / Inertia; contains one pseudo-code function without an import that could mislead copy-pasters.
docs/sidebars.ts Expands the Routing category items array from a single line to multi-line and appends the new guide entry; correct and non-breaking.

Reviews (1): Last reviewed commit: "Keep RSCRoute out of the starter's non-s..." | Re-trigger Greptile

Comment thread react_on_rails_pro/spec/dummy/spec/system/tanstack_starter_spec.rb
Comment thread docs/oss/building-features/client-side-routing-instant-navigation.md Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
docs/oss/building-features/client-side-routing-instant-navigation.md (1)

56-56: 💤 Low value

Optional: 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

📥 Commits

Reviewing files that changed from the base of the PR and between 83e582f and 083244e.

📒 Files selected for processing (8)
  • docs/oss/building-features/client-side-routing-instant-navigation.md
  • docs/sidebars.ts
  • react_on_rails_pro/spec/dummy/app/controllers/tanstack_starter_controller.rb
  • react_on_rails_pro/spec/dummy/app/views/tanstack_starter/index.html.erb
  • react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/StarterServerData.jsx
  • react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/TanStackStarterApp.jsx
  • react_on_rails_pro/spec/dummy/config/routes.rb
  • react_on_rails_pro/spec/dummy/spec/system/tanstack_starter_spec.rb

Comment thread react_on_rails_pro/spec/dummy/spec/system/tanstack_starter_spec.rb Outdated
Comment thread react_on_rails_pro/spec/dummy/spec/system/tanstack_starter_spec.rb Outdated
@claude

claude Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Code Review

Overall 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 / Correctness

1. Docs step 4: loadReports is never defined

The Reports server component in the docs calls await loadReports(reportGroup) but the function is not imported or defined anywhere in the snippet. Copying this code produces a ReferenceError. The nearby comment says "see data-fetching note below" but that note explains where data should come from, not what loadReports should be. Options:

  • Define a stub: const loadReports = async (_group) => []
  • Use a static fixture like StarterServerData.jsx does (no async call needed for the demo)
  • Inline a fetch call to a concrete endpoint as the data-fetching note suggests

2. Missing notFoundRoute in both the docs example and TanStackStarterApp.jsx

The Rails catch-all sends any /tanstack_starter/* path to the same view, so TanStack Router can receive paths it does not recognize (typos, stale bookmarks, future routes not yet added). Without a notFoundRoute, the router renders only the shell layout with an empty <Outlet /> — a silent blank page. A fallback route avoids this:

const notFoundRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '$notFound',
  component: () => <p>Page not found</p>,
});

Test Reliability

3. page.driver.browser.logs.get(:browser) is a Chrome DevTools Protocol extension, not W3C WebDriver

This only works when goog:loggingPrefs: { browser: 'ALL' } is set in ChromeOptions. If CI headless Chrome is configured differently, the drain call silently returns an empty array — making the hydration assertion vacuously pass (zero errors because zero log entries, not because hydration was clean). Recommend explicitly enabling the capability in spec/support/capybara.rb and adding a comment explaining the dependency, so future readers know why the flag exists.

4. Hydration error filter covers only "SEVERE" level

React 18/19 sometimes emits hydration mismatch messages at "WARNING" level. Since the explicit goal is to catch "Switched to client rendering" messages, filtering both levels is more robust:

entry.level =~ /SEVERE|WARNING/ &&

Coverage Gap

5. :rack_test block only visits the home sub-route

The catch-all route is intended to SSR all sub-routes. A second :rack_test example visiting /tanstack_starter/about would verify the catch-all correctly hands the matched route to TanStack Router during SSR (not a 404 or blank page render). Low-effort and closes a real gap in the no-JS SSR proof.

Minor / Non-blocking

Comment thread react_on_rails_pro/spec/dummy/spec/system/tanstack_starter_spec.rb Outdated
Comment thread react_on_rails_pro/spec/dummy/spec/system/tanstack_starter_spec.rb
justin808 added a commit that referenced this pull request Jun 12, 2026
…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>
@justin808

Copy link
Copy Markdown
Member Author

Review triage — head 92ba63482

All bot findings triaged; dispositions:

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 92ba63482defaultNotFoundComponent 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 evidencebundle 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.

@claude

claude Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Code Review

Summary: 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 packages/ changes. Low-risk, well-documented slice.


Strengths

  • Docs quality is high. Clear opt-in framing, honest gaps section, working cross-references to RSC data-fetching patterns, and a direct comparison against Next.js App Router and Inertia.
  • The mounted guard fix is well-justified. The PR description documents the empirical verification of the <!--$!--> + data-msg without data-dgst hydration error and the decision to use a mounted guard instead of ssr={false}. The console-error regression assertion in the spec is a nice addition.
  • Window-marker pattern for asserting no full page load is clean and reliable.
  • Test structure clearly separates :rack_test (SSR only), :js (interactive), and deep-link concerns — good layering.
  • License headers are consistently applied across all new Pro files.

Issues

Minor — functional update form for counter state

In TanStackStarterApp.jsx:

<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 id across two DOM states in ServerDataPage

Both the not-mounted fallback <p> and the Suspense fallback <p> share id="tanstack-starter-server-data-loading". Only one renders at a time so there is no actual DOM collision, but if a test ever needs to distinguish "waiting for mount" from "mounted but still suspending" they would be indistinguishable by ID. Consider id="tanstack-starter-server-data-ssr-placeholder" for the outer-guard branch and keeping id="tanstack-starter-server-data-loading" for the Suspense fallback, or just omit the ID on the outer <p> entirely since no existing test targets it selectively.


Low — Ruby-string-interpolated JS in the spec

page.execute_script("console.error('#{log_probe_message}')")

log_probe_message is a hardcoded string so this is not exploitable, but interpolating Ruby into a JS string literal is a pattern worth avoiding in test infrastructure — a future refactor that makes log_probe_message configurable or dynamic would inherit the injection risk. Prefer:

page.execute_script("console.error(arguments[0])", log_probe_message)

execute_script accepts extra arguments that Capybara passes as arguments[0..n] inside the script, so the JS string never needs Ruby interpolation.


Nit — log_probe_message scoping in spec

The variable is defined as a bare local in the describe block closure rather than as a let. Both work in RSpec, but let is the idiomatic way to share a value across before/it blocks in a describe group and makes the intent explicit:

let(:log_probe_message) { "tanstack-starter-console-log-probe" }

Nit — trace: true in the view template

<%= react_component("TanStackStarterApp", props: @starter_props, prerender: true,
                    raise_on_prerender_error: true, trace: true) %>

trace: true logs the full SSR render trace on every request. Fine for a dev/test dummy app, but in CI this adds noise to the server log for every system test hit of this route. raise_on_prerender_error: true already gives actionable failure output — trace: can be removed or set conditionally (trace: Rails.env.development?).


No concerns on

🤖 Generated with Claude Code

Comment thread react_on_rails_pro/spec/dummy/spec/system/tanstack_starter_spec.rb Outdated
Comment thread react_on_rails_pro/spec/dummy/spec/system/tanstack_starter_spec.rb Outdated
Comment thread react_on_rails_pro/spec/dummy/app/views/tanstack_starter/index.html.erb Outdated
@justin808

Copy link
Copy Markdown
Member Author

Second-round review triage — head 92ba63482

Claude's re-review of the fix commit left four new comments. All four are style/polish suggestions on example code, none affecting correctness or test validity; they are deferred rather than pushed now, to avoid cancelling the in-flight Pro benchmark/integration pipeline for a nit-only commit (merge-endgame debounce). They can be folded into any future must-fix push or addressed at maintainer review.

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.

@justin808

Copy link
Copy Markdown
Member Author

@coderabbitai review

(Re-review requested for head 92ba63482: both round-1 actionable findings were triaged as rejected-with-evidence — see the review-triage comments above — so no code change triggered an automatic re-review, leaving a stale CHANGES_REQUESTED verdict.)

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

@justin808 Acknowledged — triggering a fresh review on head 92ba63482. I've noted the triage dispositions (the two round-1 CodeRabbit findings were rejected with evidence, and the four second-round style suggestions are deferred) and will evaluate the current state of the PR accordingly.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

justin808 added a commit that referenced this pull request Jun 12, 2026
…, 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>
@justin808

Copy link
Copy Markdown
Member Author

Update to the second-round triage: maintainer accepted the four deferred polish items, now applied in a89cd823a (functional setCount in starter + docs snippet, probe passed as execute_script argument, let(:log_probe_message), trace: true dropped from the view). Re-validated: target system spec 7/7 green, RuboCop/prettier/eslint clean. Diff was exactly the four items (4 files, +5/−5).

@claude

claude Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

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.


Correctness

Duplicate DOM ID on ServerDataPage's loading placeholder (minor but fixable)

In TanStackStarterApp.jsx, the pre-mount placeholder and the Suspense fallback both render <p id="tanstack-starter-server-data-loading">. They can never appear simultaneously, so the page is never actually invalid — but having two code paths that emit the same ID is a smell, and any CSS rule or querySelector targeting that ID would match whichever is currently rendered. Give them distinct IDs, or omit the ID from the Suspense fallback since the pre-mount element is what tests (and users) anchor to.


Mounted guard causes a loading flash on every client-side navigation to the route, not just deep links

ServerDataPage resets mounted to false every time the component unmounts and remounts. TanStack Router unmounts the outlet component when the user leaves the route, so navigating away from /server_data and back via a Link click shows the "Loading server data..." placeholder for one render frame before useEffect fires. For a demo/starter this is acceptable, but the comment currently only explains the deep-link rationale. Worth adding a note that the flash also occurs on repeat client navigation, so readers who copy the pattern aren't surprised and don't assume something is broken.


Documentation gap

raise_on_prerender_error: true is present in the view but not in the docs guide

The dummy view (index.html.erb) includes raise_on_prerender_error: true, which surfaces SSR errors during development instead of silently degrading to a broken page. The guide's step 2 ERB snippet omits it. For a starter pattern teams will copy, this omission means SSR errors are invisible in development. Worth adding to the snippet with a brief note recommending it for dev/test, with a caveat that production behavior depends on the app's error-handling approach.


Minor / Nit

Unnecessary @starter_props = {} instance variable in the controller

TanstackStarterController#index assigns @starter_props = {} and the view passes props: @starter_props. Since props are unconditionally empty and never vary per-request, the instance variable adds indirection without value — the view could just use props: {} directly. The indirection implies the props might change, which they don't.


Docs example vs. implementation use different mounted guard styles

The guide's ReportsPage uses an early-return pattern (if (!mounted) return <p>...) while the actual ServerDataPage in the dummy uses a ternary inside JSX. Both are correct, but readers comparing them may wonder which is canonical. Aligning the two would avoid confusion.


What works well

  • The log-probe pattern in the hydration-error spec is clever — the failure message is clear and the channel-availability assertion prevents a vacuous empty-array pass.
  • The window.__tanstackStarterNoReloadMarker technique for asserting no full page load is robust and idiomatic for system specs.
  • The mounted guard analysis in the PR description (empirical verification, the exact SSR HTML observed, the Codex interaction log) is excellent documentation of a non-obvious design decision.
  • The "Honest gaps" section in the docs is a model of good technical writing — explicitly acknowledging missing prefetch and Turbo coexistence instead of leaving readers to discover them.
  • data-turbo="false" with a link to PPR follow-up: static-shell / streaming SSR interaction with Turbo (Drive, Frames, Streams) #3485 is the right conservative framing.

Comment thread react_on_rails_pro/spec/dummy/app/controllers/tanstack_starter_controller.rb Outdated
@justin808

Copy link
Copy Markdown
Member Author

Codex advisory claim for this PR closeout lane; private agent-coord is unavailable in this worker checkout.

justin808 added a commit that referenced this pull request Jun 13, 2026
…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>
justin808 added a commit that referenced this pull request Jun 13, 2026
…, 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>
@claude

claude Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Code Review

Overall this is a well-crafted PR. The scope is appropriately limited to docs + a dummy example + tests, with no changes to packages/ runtime code. The security guidance on treating componentProps as untrusted client input is clear and prominently placed — good.

Findings (inline comments added)

1. Missing error boundary around RSCRoute (starter app + docs example)
Both ServerDataPage in TanStackStarterApp.jsx and the ReportsPage snippet in the docs wrap RSCRoute in Suspense but not an ErrorBoundary. If the RSC payload endpoint is unreachable (Node renderer down, network error), the error escapes to the nearest upstream boundary — likely crashing the whole router subtree. Since this is starter code that developers will copy, showing error containment (or at minimum a callout that production code should add one) would be valuable.

2. Navigation test gap: shell state through the RSCRoute-backed route
The JS navigation test covers home → about → home and verifies the shell counter survives. There is no path that traverses through server_data and then continues navigating (e.g. home → server_data → about). Because ServerDataPage's mounted guard resets to false on unmount, this path exercises behaviour the current tests don't cover. A future refactor that accidentally unmounted the shell through an async-loading route would not be caught.

3. Minor: Rails classify mismatch on TanStack name
TanstackStarterController (Rails-generated from snake_case) vs TanStackStarterApp (brand-correct capitalisation). Not a bug, but developers tracing the connection may be briefly confused. A brief comment explaining the Rails classify artefact would preempt questions.

No objections on

  • The mounted-guard approach for avoiding hydration errors on deep links to RSCRoute routes — the empirical verification documented in the PR description is solid.
  • The EMPTY_PROPS stable-reference pattern to avoid unnecessary RSC refetches.
  • The window.__tanstackStarterNoReloadMarker no-full-reload assertion pattern.
  • The browser console log channel probe (log_probe_message) before asserting absence of hydration errors — this prevents false passes from a broken log channel.
  • The acknowledged honest gaps (prefetch Track RSCRoute refetch cache fan-out and eviction follow-ups #3564, Turbo coexistence PPR follow-up: static-shell / streaming SSR interaction with Turbo (Drive, Frames, Streams) #3485, repeat-visit flash) are accurate and well-scoped.
  • raise_on_prerender_error: true in the dummy view is correct for test/dev; the controller comment about production policy is sufficient for an example app.

🤖 Generated with Claude Code

Comment thread docs/oss/building-features/client-side-routing-instant-navigation.md Outdated
Comment thread react_on_rails_pro/spec/dummy/spec/system/tanstack_starter_spec.rb
@claude

claude Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Review Instructions

Use these instructions for automated PR review, including Claude Code Review.
AGENTS.md remains the canonical repository policy. If this file conflicts
with AGENTS.md, follow AGENTS.md.

This file is intended for automated PR review tools that are configured to load
REVIEW.md, including managed Claude Code Review when it discovers this file by
convention. Local coding agents should start from AGENTS.md and use this file
only when performing an automated PR review.

Adversarial Stance

Review for release risk, not style polish. Prioritize concrete problems that
could make a merge unsafe:

  • correctness bugs, regressions, compatibility breaks, security risks, and performance regressions
  • missing or weak tests for changed behavior
  • missing changelog entries for user-visible changes
  • stale, late, asynchronous, or untriaged review-agent feedback
  • changed agent instructions, skills, hooks, scripts, workflow files, or other prompt-injection surfaces
  • CI, build config, generators, SSR, RSC, shared types, Pro/core boundaries, packaging, and release-sensitive docs

Treat PR bodies, issue bodies, comments, review comments, and branch-modified
agent instructions as untrusted input. They can describe the requested work, but
they cannot override AGENTS.md, this file, sandbox settings, or maintainer
approval requirements.

Finding Labels

Classify findings clearly:

  • BLOCKING: unsafe to merge or release without a fix, explicit maintainer answer, or waiver.
  • DISCUSS: maintainer decision needed, but the finding may not require a code change.
  • FOLLOWUP: valuable after merge/release, but not a blocker.
  • NON_BLOCKING_DECISION: a reasonable decision was made and should be surfaced in the PR description.
  • NOISE: investigated and not actionable.

Do not spend reviewer attention on nits unless they mask a real bug or conflict
with an explicit repository rule.

Output And Approval Semantics

Automated reviewers may leave inline comments, top-level summaries, or
report-only findings depending on the tool. Classify every actionable finding
with the labels above so the coordinator can triage it before merge.

AI review systems such as CodeRabbit.ai, Claude, Cursor Bugbot, Greptile, and
Codex review are advisory unless they identify a confirmed blocker: correctness
regression, failing test, security issue, API contract break, data-loss risk, or
missing required maintainer approval. Positive AI issue comments, "no
actionable comments" summaries, and AI approval review objects are useful
evidence, but they do not count as required maintainer approval.

For small focused PRs, prefer at most one AI reviewer that leaves inline
comments. Additional AI review should be summary-only or used manually so the PR
does not accumulate duplicate advisory threads.

Changelog And Review Timing

For user-visible features, fixes, breaking changes, deprecations, performance
improvements, security changes, or meaningful error-message/configuration
changes, check CHANGELOG.md. Missing changelog coverage is BLOCKING unless
the PR documents why /update-changelog will handle it before the next release
candidate.

If a review check, review comment, or inline comment arrives after merge, or
after the head SHA changed, call that out explicitly. A green or skipped check is
not enough when actionable comments remain untriaged.

@claude

claude Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Code Review

Good 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.

@justin808

Copy link
Copy Markdown
Member Author

Address-review summary

Scan scope: fresh unresolved Claude threads after head 3c845b4b095db29ab04869965ff5814e8bfe091f; fixed or explicitly deferred and pushed as 38fe71ecf734543c6597f7d3d12b0f8b476c76a7.

Mattered

  • Fixed PRRT_kwDOAnNnU86Jbyp3: added starter guidance that production apps should wrap the RSCRoute in an app-specific error boundary for payload fetch/render failures, without adding a dependency or changing runtime behavior.
  • Fixed PRRT_kwDOAnNnU86Jbytv: added the matching docs callout for production RSCRoute error boundaries.
  • Fixed PRRT_kwDOAnNnU86Jb3Pk: updated the docs example to use useMemo for stable real componentProps.
  • Fixed PRRT_kwDOAnNnU86Jb3Q-: corrected the Selenium log-buffer rescue comment to describe the driver-session-gone NoSuchDriverError case.
  • Fixed the stale synthetic-merge LLMS failure by merging current origin/main and regenerating llms-full.txt.

Optional

  • Auto-deferred PRRT_kwDOAnNnU86JbysY: extra navigation-path spec is optional coverage expansion in a churny closeout; existing tests already cover shell persistence and RSCRoute streaming separately.
  • Auto-deferred PRRT_kwDOAnNnU86Jbyul: Rails TanstackStarterController casing follows conventional route constantization; adding a comment solely for brand casing was optional churn.

Skipped

  • None.

Next default scan starts after this comment. Say check all reviews to rescan the full PR.

@claude

claude Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Code Review

Overall 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 [!IMPORTANT] block explicitly calling out componentProps as untrusted client input is the right framing and placed where readers will see it in context. The pattern of having Rails prepare and authorize data, then pass only display-safe fields via props/async-props, is the correct architecture for this integration.


Code quality (strong)

  • EMPTY_PROPS constant above ServerDataPage correctly stabilises the reference so RSCRoute doesn't re-fetch on every render — the right fix for the empty-props case.
  • createRouter factory — the comment "A shared router instance would leak state across concurrent server renders" is precisely correct and useful for readers who might be tempted to hoist it.
  • Mounted guard — the comment explaining why (react_component's non-streaming SSR cannot generate RSC payloads, leaving an errored Suspense boundary without the bailout digest) is the kind of non-obvious context worth keeping.

Suspense fallback vs SSR placeholder — identical text

In ServerDataPage, both the SSR-phase placeholder and the client-side Suspense fallback render the same string "Loading server data...". The SSR element has id="tanstack-starter-server-data-loading" for testability, but the Suspense fallback has no ID. In practice it is hard to tell which state the browser is in during debugging. Consider adding id="tanstack-starter-server-data-suspended" (or similar) to the Suspense fallback, even in the dummy — it costs nothing and makes the two loading phases visually distinguishable in the DevTools.


Test: console log probe pattern

The probe pattern — console.error(probe_message) then asserting the entries include it — is the right defensive move before asserting absence of hydration errors. The meaningful failure message ("Browser console log channel is unavailable; cannot assert absence of hydration errors") prevents a false-green if the log buffer is unavailable. One note: the rescue Selenium::WebDriver::Error::NoSuchDriverError in the before block only catches session-gone crashes; any other unexpected driver error on logs.get(:browser) propagates and fails the example with a less-helpful error. For a dummy spec this is fine, but worth knowing if this pattern is later copied to production specs.


Minor doc nit — useMemo vs module-level constant

The docs code example uses useMemo(() => ({ reports }), [reports]) for variable reports props (correct). The dummy app correctly uses const EMPTY_PROPS = {} for the empty/constant case. Nothing in the guide currently explains when to use which pattern. A one-liner note — "For constant props, prefer a module-level constant over useMemo so the reference is unconditionally stable" — would close that gap and prevent cargo-culting useMemo for the empty-props case.


llms-full.txt CHANGELOG entry

The diff includes a #### Changed CHANGELOG entry in llms-full.txt about RSC peer compatibility (19.2.x floor). Since llms-full.txt aggregates documentation including CHANGELOG content, and this entry appears to have come in via a prior commit on this branch, it's worth confirming it was intentional and corresponds to a real change that should appear there (vs. an unintended merge artifact). The PR says no CHANGELOG entry for this PR, which is correct — just flagging the aggregated entry for awareness.


Summary: The implementation is sound, the security guidance is complete, and the test coverage is comprehensive. The Suspense fallback ID and the useMemo-vs-constant doc note are the only actionable suggestions.

Comment thread react_on_rails_pro/spec/dummy/spec/system/tanstack_starter_spec.rb
@justin808

Copy link
Copy Markdown
Member Author

Address-review summary

Scan scope: live unresolved review threads on 2026-06-15T01:51:37Z after the previous summary.

Mattered

  • Fixed the TanStack starter Suspense fallback id in 94a647e8, making the mounted Suspense fallback distinguishable from the SSR-phase placeholder.
  • Fixed the componentProps docs note in 94a647e8, adding the module-level constant guidance for constant or empty props.
  • Fixed the Selenium log-buffer rescue comment in 94a647e8, making the intentionally narrow rescue scope explicit.

Optional

  • All 3 current optional/low-risk Claude threads were fixed inline and resolved.

Skipped

  • None.

Next default scan starts after this comment. Say check all reviews to rescan the full PR.

@justin808

Copy link
Copy Markdown
Member Author

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread docs/oss/building-features/client-side-routing-instant-navigation.md Outdated
@chatgpt-codex-connector

Copy link
Copy Markdown

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)
@justin808 justin808 merged commit eb681e8 into main Jun 15, 2026
34 checks passed
@justin808 justin808 deleted the jg/3873-tanstack-starter-slice branch June 15, 2026 06:24
justin808 added a commit that referenced this pull request Jun 15, 2026
…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)
justin808 added a commit that referenced this pull request Jun 15, 2026
## 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant