Skip to content

Add TanStack Router demo with SSR and TypeScript#104

Open
justin808 wants to merge 16 commits intomainfrom
jg/tanstack-router-demo
Open

Add TanStack Router demo with SSR and TypeScript#104
justin808 wants to merge 16 commits intomainfrom
jg/tanstack-router-demo

Conversation

@justin808
Copy link
Member

@justin808 justin808 commented Jan 14, 2026

Summary

  • Add new demo app basic-v16-rspack-tanstack showcasing TanStack Router with React on Rails
  • Demonstrates file-based routing with SSR support
  • Full TypeScript integration
  • Uses Rspack with SWC for fast builds

Features Demonstrated

  • File-based routing: Routes defined as files in src/routes/
  • Server-side rendering: Initial HTML rendered on server with correct route
  • URL parameters: Dynamic route segments like /users/:userId
  • Search parameters: Type-safe query string handling at /search
  • Nested routes: Nested layouts with outlet at /demo/nested
  • Client-side navigation: Navigate without full page reloads after hydration

Technical Stack

  • Rails 8.0.3 with PostgreSQL
  • React on Rails 16.2.0.rc1
  • TanStack Router for routing
  • Rspack with SWC transpiler
  • TypeScript
  • Playwright E2E tests

Test Plan

  • Build passes: bin/shakapacker compiles without errors
  • RuboCop passes: bundle exec rubocop
  • E2E tests pass: npx playwright test (after starting dev server)
  • Manual verification:
    • Home page renders with SSR at /
    • About page renders at /about
    • Users list at /users with links to detail pages
    • URL params work at /users/123
    • Search params work at /search?q=test&page=2
    • Nested routes work at /demo/nested/deep
    • Navigation works without full page reloads

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • New Rails demo: React on Rails v16 with TanStack Router, SSR, client-side navigation, file-based & nested routes, dynamic params, search, users list/detail, and a HelloWorld example; includes polished static error pages.
  • Tests

    • Playwright E2E suite verifying SSR, hydration, routing, query params, and nested-route behavior.
  • Documentation

    • README and setup guides for installation, SSR Node renderer, dev workflows, and testing.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 14, 2026

Warning

Rate limit exceeded

@justin808 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 22 minutes and 43 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 85f38bc and 4f917af.

⛔ Files ignored due to path filters (3)
  • demos/basic-v16-rspack-tanstack/Gemfile.lock is excluded by !**/*.lock
  • demos/basic-v16-rspack-tanstack/package-lock.json is excluded by !**/package-lock.json
  • demos/basic-v16-rspack-tanstack/react-on-rails-pro/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • demos/basic-v16-rspack-tanstack/.gitignore
  • demos/basic-v16-rspack-tanstack/Gemfile
  • demos/basic-v16-rspack-tanstack/app/javascript/packs/server-bundle.js
  • demos/basic-v16-rspack-tanstack/app/javascript/src/node-polyfills.ts
  • demos/basic-v16-rspack-tanstack/config/initializers/react_on_rails_pro.rb
  • demos/basic-v16-rspack-tanstack/package.json
  • demos/basic-v16-rspack-tanstack/react-on-rails-pro/node-renderer.js
  • demos/basic-v16-rspack-tanstack/react-on-rails-pro/package.json

Walkthrough

Adds a complete new Rails 8 demo app "basic-v16-rspack-tanstack" integrating React on Rails v16 + ReactOnRails Pro Node renderer, Rspack/Shakapacker, TanStack Router (file-based + SSR), TypeScript, Playwright e2e tests, many bin/Procfile utilities, Webpack/Rspack config, and extensive docs/metadata.

Changes

Cohort / File(s) Summary
Metadata & docs
demos/basic-v16-rspack-tanstack/.demo-metadata.yml, README.md, docs/react-on-rails-pro-setup.md, demos/.../.dev-services.yml.example, demos/.../.env.example
New demo metadata, README and setup docs, dev-services example, and env example.
Project config & tooling
demos/.../.gitignore, lefthook.yml, Gemfile, package.json, tsconfig.json, babel.config.js, playwright.config.ts
Project-level configs, dependencies, TS/Babel setup, Playwright config, and updated pre-commit RuboCop logic in lefthook.
Procfiles & bin scripts
Procfile.dev, Procfile.dev-prod-assets, Procfile.dev-static-assets, bin/* (many new scripts)
Dev Procfiles + numerous bin executables (dev launcher, bundler stub, shakapacker scripts, switch-bundler, setup, rubocop/brakeman wrappers, precompile hook, etc.).
Rails bootstrap & config
config/application.rb, config/boot.rb, config/environment.rb, Rakefile, config.ru
Standard Rails app boot and application class added.
Environments & initializers
config/environments/*.rb, config/initializers/*
Full dev/test/prod environment files and many initializers, including React on Rails and ReactOnRailsPro SSR/node-renderer configuration.
Shakapacker / bundler configs
config/shakapacker.yml, config/webpack/* (commonWebpackConfig.js, clientWebpackConfig.js, serverWebpackConfig.js, generateWebpackConfigs.js, development.js, production.js, test.js, webpack.config.js)
Multi-environment Shakapacker and webpack/rspack factory code, bundler detection, TanStack plugin wiring, server-bundle isolation and SSR-specific loader/plugin adjustments.
Rails routes, controllers, views & assets
config/routes.rb, app/controllers/*, app/views/layouts/*, app/views/tanstack_app/index.html.erb, app/assets/stylesheets/application.css
Routes wired to TanstackAppController#index for SSR, layouts, PWA manifest/service-worker scaffolds, and CSS manifest.
App base classes & tests
app/models/application_record.rb, app/jobs/application_job.rb, app/mailers/application_mailer.rb, app/helpers/application_helper.rb, test/test_helper.rb
Standard application base classes and test helper scaffolding.
React + TypeScript app & routes
app/javascript/packs/*, app/javascript/src/router.ts, src/TanStackApp/ror_components/*.{client,server}.tsx, src/HelloWorld/ror_components/*, src/routes/**, app/javascript/types/css-modules.d.ts
Full TanStack Router file-based routes, client/server TanStackApp components (server calls router.load() for SSR sync), HelloWorld sample component, CSS modules typings — many route components to review for SSR/params handling.
Server renderer package
react-on-rails-pro/node-renderer.js, react-on-rails-pro/package.json
Node renderer entrypoint and package for ReactOnRailsPro with env-driven config and worker settings.
E2E tests
e2e/tanstack_router.spec.ts, playwright.config.ts
Playwright end-to-end tests covering SSR, hydration, client navigation, and hydration diagnostics.
Public static assets & error pages
public/*.html, public/robots.txt
Static error pages (400/404/422/500/406) and robots.txt.
Misc: DB, seeds, credentials, Puma, locales
db/seeds.rb, config/credentials.yml.enc, config/database.yml, config/puma.rb, config/locales/en.yml
Database config, encrypted credentials placeholder, puma, seeds/comments, and locale file.

Sequence Diagram(s)

sequenceDiagram
    participant Browser as Client
    participant Rails as Rails Server
    participant Node as Node Renderer
    participant React as React App

    Browser->>Rails: GET /users/123
    Rails->>React: instantiate TanStackApp.server with initialUrl
    React->>Node: request SSR render (server bundle)
    Node->>React: rendered HTML + hydration payload
    React-->>Rails: return server-rendered HTML
    Rails-->>Browser: HTML response (prerendered)
    Browser->>React: hydrate with TanStackApp.client
    Browser->>React: client navigation -> router handles route (no full reload)
Loading
sequenceDiagram
    participant Dev as Developer
    participant Shakapacker as Bundler (Rspack/Webpack)
    participant Rails as Rails App

    Dev->>Shakapacker: start dev (bin/shakapacker or bin/shakapacker-dev-server)
    Shakapacker->>Shakapacker: build client bundle (HMR)
    Shakapacker->>Shakapacker: build server bundle -> private_output_path
    Shakapacker-->>Rails: emit routeTree.gen.ts, server-bundle.js
    Rails->>Node: Node renderer uses server-bundle for SSR
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 In my burrow I scaffold with a hop and a cheer,
Routes lined like carrots, SSR drawing near,
Rspack spins the bundles, Node hums the tune,
React wakes and hydrates beneath the moon,
Hop—demo delivered, tails twitching in gear.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add TanStack Router demo with SSR and TypeScript' clearly and concisely summarizes the main objective of this PR, which is to introduce a new demo application featuring TanStack Router integration with server-side rendering and TypeScript support.

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jg/tanstack-router-demo

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.

@claude
Copy link

claude bot commented Jan 14, 2026

PR Review: TanStack Router Demo with SSR and TypeScript

Great work on this comprehensive demo! The implementation showcases TanStack Router integration with React on Rails in an exemplary way.

✅ Strengths

1. Excellent SSR Implementation

  • Router factory pattern (router.ts:11-28) properly handles server/client differences with memory history for SSR and browser history for client
  • Server component (TanStackApp.server.tsx:11-14) correctly calls router.load() before rendering
  • Client component uses React.useState callback to create router once, preventing recreation on re-renders
  • Props passing is clean with initialUrl from Rails controller

2. Well-Structured Configuration

  • Webpack configs properly separate client/server concerns
  • TanStack Router plugin only applied to client bundle with proper error handling (clientWebpackConfig.js:18-33)
  • Server bundle configuration correctly disables CSS extraction and uses exportOnlyLocals for CSS modules
  • Good use of Shakapacker 9.0+ privateOutputPath with helpful validation warnings

3. Comprehensive Testing

  • E2E tests cover all major use cases: SSR verification, client hydration, navigation, URL/search params
  • Hydration mismatch detection (tanstack_router.spec.ts:98-114) is excellent - catches React hydration errors
  • Tests verify SSR works at specific routes, not just after client navigation

4. TypeScript Configuration

  • Strict mode enabled with noUncheckedIndexedAccess - great for type safety
  • Proper module resolution with bundler mode
  • Clean type registration for TanStack Router

🔍 Observations & Suggestions

1. Potential SSR/Client Mismatch (Minor)

Location: TanStackApp.client.tsx:9

The client component ignores the initialUrl prop - this is actually fine since the browser will use its current location, but the prop definition is misleading.

Suggestion: Either remove initialUrl? from the interface or document why it's intentionally unused on the client.

2. Missing routeTree.gen.ts Documentation

The .gitignore correctly excludes routeTree.gen.ts, but there's no note in the README about running the TanStack Router plugin to generate it.

Suggestion: Add setup instructions to the README mentioning bin/shakapacker generates this file.

3. TypeScript Strictness Enhancement

Great use of noUncheckedIndexedAccess! Consider adding noUnusedLocals, noUnusedParameters, and exactOptionalPropertyTypes for even stricter type safety.

🔒 Security Review

✅ Good Practices

  1. Server bundle security: enforce_private_server_bundles = true prevents exposure of server-side code
  2. Output paths: Server bundle correctly goes to ssr-generated/ (private directory), not public/
  3. Browser restrictions: allow_browser versions: :modern in ApplicationController
  4. No credentials committed: .gitignore properly excludes .env* and config/master.key

No Issues Found

  • No XSS vulnerabilities (React handles escaping)
  • No SQL injection (no database queries in this demo)
  • No hardcoded secrets
  • No unsafe third-party dependencies flagged

📊 Performance Considerations

✅ Optimizations Present

  1. Rspack + SWC: Fast build times (20x faster than Babel per PR description)
  2. Code splitting: autoCodeSplitting: true in TanStack Router plugin
  3. Preload strategy: defaultPreload: intent loads routes on hover/focus
  4. Scroll restoration: Enabled in router config
  5. Production optimizations: Server bundle uses eval sourcemaps (fast) only in dev

🧪 Test Coverage Assessment

Well Covered

  • ✅ SSR rendering at all routes
  • ✅ Client-side navigation
  • ✅ URL params extraction
  • ✅ Search params handling
  • ✅ Nested routes
  • ✅ Hydration consistency

For a demo app, the E2E coverage is excellent.

📝 Code Quality

✅ Excellent

  • Consistent formatting
  • Clear component organization (ror_components/ pattern)
  • Good separation of concerns (client/server components)
  • Helpful comments in configs explaining why
  • Demo routes have educational explanations built-in

Minor Nitpicks

  1. HelloWorld component still present but unused (leftover from scaffolding). Could be removed if not needed.
  2. CSS modules type definition (css-modules.d.ts) is good, but HelloWorld.module.css is the only usage

🎯 Alignment with CLAUDE.md Guidelines

✅ Follows Best Practices

  1. No install commands run - Configuration files are custom and preserved ✅
  2. Version updates only - Gemfile and package.json updated appropriately ✅
  3. Custom webpack configs preserved - No defaults overwritten ✅
  4. Proper documentation - Demo metadata file included ✅

🏁 Final Recommendation

APPROVE

This is a high-quality demo that effectively showcases:

  • TanStack Router with file-based routing
  • Proper SSR implementation with React on Rails
  • Rspack/SWC integration for fast builds
  • Comprehensive E2E testing
  • TypeScript best practices

Before Merging (Optional)

  • Remove unused HelloWorld component if not needed
  • Add setup instructions to README for routeTree.gen.ts generation
  • Consider documenting why initialUrl is unused on client

After Merging

  • Run E2E tests to verify: npx playwright test
  • Verify RuboCop passes: bundle exec rubocop
  • Test manual verification checklist from PR description

Great work showcasing React on Rails 16.2.0.rc1 capabilities! This demo will be valuable for users wanting to integrate TanStack Router with SSR.


Reviewed by: Claude Code
Review Date: 2026-01-14

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 13

🤖 Fix all issues with AI agents
In `@demos/basic-v16-rspack-tanstack/app/controllers/application_controller.rb`:
- Around line 1-4: Add an explicit CSRF protection call in ApplicationController
by invoking protect_from_forgery with an appropriate strategy (e.g.,
protect_from_forgery with: :exception) so the controller explicitly enforces
CSRF defenses; update the ApplicationController class (where allow_browser is
set) to call protect_from_forgery to make the protection explicit and prevent
accidental bypass in the future.

In `@demos/basic-v16-rspack-tanstack/app/javascript/src/router.ts`:
- Around line 11-29: The client component is creating the router via
React.useState(() => createRouter()) but never passes the component's initialUrl
prop; update the client-side initialization to call createRouter with the
initialUrl (i.e., React.useState(() => createRouter({ initialUrl }))) so the
createRouter function receives and uses opts.initialUrl when building memory
history; locate the client component where React.useState and createRouter are
used and pass the initialUrl prop through.

In `@demos/basic-v16-rspack-tanstack/app/javascript/src/routes/search.tsx`:
- Around line 24-27: The local inputValue state in SearchPage is initialized
from Route.useSearch().q only once and can desync when the URL changes (e.g.,
browser back/forward); add a React.useEffect that watches q and calls
setInputValue(q ?? '') so the text input updates whenever the search param
changes, leaving user typing behavior unchanged; reference SearchPage,
Route.useSearch, inputValue, and setInputValue when making the change.
- Around line 13-18: The page computation currently uses parseInt on string
values but doesn't guard against NaN; update the logic around the page
assignment (the expression handling search.page and parseInt) to detect when
parseInt returns NaN and fall back to 1 (e.g., parse the string then check
Number.isFinite or isNaN and return 1 on invalid result). Ensure you keep the
existing branches (numeric, string, default) but after parsing the string branch
validate the parsed value before assigning it to page.

In
`@demos/basic-v16-rspack-tanstack/app/javascript/src/TanStackApp/ror_components/TanStackApp.server.tsx`:
- Around line 9-17: The component calls router.load() but doesn't await it, so
route loaders may not finish before render; change the code to await
router.load({ sync: true }) before returning JSX by making TanStackApp an async
Server Component (or move router creation/loading to the server entry that calls
renderToString) — specifically, ensure createRouter(...) is followed by await
router.load({ sync: true }) (or perform that load in your wrapper) and only then
return <RouterProvider router={router} /> so the RouterProvider receives a fully
preloaded router.

In `@demos/basic-v16-rspack-tanstack/app/views/layouts/tanstack_app.html.erb`:
- Line 8: The stylesheet_pack_tag call is currently parameterless; change it to
explicitly specify the entry point name to match application.html.erb (use
stylesheet_pack_tag 'application') and likewise ensure any javascript_pack_tag
in this layout uses javascript_pack_tag 'application' so both layouts
consistently reference the same explicit entry entry point names (look for
stylesheet_pack_tag and javascript_pack_tag in this file and
application.html.erb to align them).

In `@demos/basic-v16-rspack-tanstack/bin/dev`:
- Line 27: DEFAULT_ROUTE is stale (set to "hello_world") and must be updated to
match the app's current TanStack Router routes: change the DEFAULT_ROUTE
constant to the actual default path (e.g., "/" ) or remove the constant if the
script no longer needs a default; after editing, verify any usage of
DEFAULT_ROUTE in the script still works and cross-check available routes in the
demo routing config (search for route definitions like root or any route names
in config/routes.rb) to ensure the value matches.

In `@demos/basic-v16-rspack-tanstack/bin/shakapacker-precompile-hook`:
- Around line 23-29: The precompile hook script (shakapacker-precompile-hook)
calls Rainbow (e.g., Rainbow("🔄 Running React on Rails precompile hook..."))
but the rainbow gem is not declared, causing a NameError at runtime; add gem
"rainbow" to the project's Gemfile and run bundle install (or update your
lockfile) so Rainbow is available when the hook runs.

In `@demos/basic-v16-rspack-tanstack/bin/switch-bundler`:
- Around line 47-62: The YAML.load_file usage in update_shakapacker_config is
unsafe; replace it with YAML.safe_load(File.read(`@shakapacker_config`),
permitted_classes: [...], permitted_symbols: [...], aliases: true) (or
appropriate permitted_classes/symbols for your config) to parse the file safely,
then proceed to update config["default"]["assets_bundler"] and
config["default"]["webpack_loader"] as before and write back with YAML.dump;
ensure you still abort if File.exist? is false and preserve the same variables
(`@shakapacker_config`, `@target_bundler`, update_shakapacker_config).

In `@demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js`:
- Around line 100-110: The find callback for rule.use that computes cssLoader
can throw because testValue may be undefined when item is an object without a
loader property; update the callback used in rule.use.find (the code computing
testValue) to guard before calling includes by ensuring testValue is a string
(e.g., check typeof testValue === 'string' or coerce to an empty string) and
only call includes on that safe string, so cssLoader resolution never throws on
unexpected loader item shapes.
- Around line 91-99: The filter callback for rule.use can call testValue.match
when testValue may be undefined, causing a TypeError; update the callback in the
rule.use.filter (the anonymous function inside serverWebpackConfig.js) to guard
against undefined before calling match (e.g., check that testValue is a string
or truthy) and only then evaluate testValue.match(/mini-css-extract-plugin/) or
testValue === 'style-loader', so the predicate safely returns false when
testValue is undefined.

In `@demos/basic-v16-rspack-tanstack/ssr-generated/manifest.json`:
- Around line 1-13: manifest.json inside the ssr-generated output is currently
tracked despite a `/ssr-generated` rule in .gitignore; either remove the
`/ssr-generated` entry from .gitignore so the committed manifest.json stays
intentionally, or stop tracking the generated directory (untrack ssr-generated
with git rm --cached and commit) so manifest.json is no longer tracked while
keeping the files locally.

In `@demos/basic-v16-rspack-tanstack/ssr-generated/server-bundle.js`:
- Around line 1-45: The repository currently commits generated SSR assets
(ssr-generated/ including server-bundle.js and manifest.json from
react_on_rails:generate_packs) which should be ignored; add "ssr-generated/" to
.gitignore and remove the tracked files from Git (git rm --cached) so they are
no longer committed, and update the project README or deploy docs to include the
exact build command or npm/yarn script used to regenerate these files (the
project's SSR/webpack/rspack build script that produces ssr-generated) so
maintainers know how to recreate server-bundle.js and manifest.json for
deployments.
🧹 Nitpick comments (21)
demos/basic-v16-rspack-tanstack/config/initializers/react_on_rails.rb (1)

37-51: Minor: Section organization inconsistency.

Lines 50-51 (auto_load_bundle and components_subdirectory) appear after the "Test Configuration" section header but logically belong to component loading configuration. Consider moving them under the "Advanced Configuration" section or adding a dedicated "Component Loading" section for clarity.

Additionally, the comment on lines 37-46 states that build_test_command and compile: true in shakapacker.yml are "mutually exclusive," yet build_test_command is set unconditionally. If the recommended approach (compile: true) is being used in shakapacker.yml, this command may be redundant.

Suggested reorganization
  # ALTERNATIVE APPROACH: Uncomment below AND configure ReactOnRails::TestHelper
  # - Provides explicit control over test asset compilation timing
  # - Requires adding ReactOnRails::TestHelper to spec/rails_helper.rb
  # - See: https://github.com/shakacode/react_on_rails/blob/master/docs/guides/testing-configuration.md
  #
  config.build_test_command = "RAILS_ENV=test bin/shakapacker"

+  ################################################################################
+  # Component Loading
+  ################################################################################
  config.auto_load_bundle = true
  config.components_subdirectory = "ror_components"
+
  ################################################################################
  # Advanced Configuration
  ################################################################################
demos/basic-v16-rspack-tanstack/bin/setup (1)

15-19: Consider adding idempotency check for npm install.

The Ruby dependency step uses bundle check to skip unnecessary installs, but npm install runs unconditionally every time. For consistency and faster repeated runs, consider a similar pattern:

♻️ Optional: Add idempotency check for npm
   system("bundle check") || system!("bundle install")

   # Install JavaScript dependencies
-  system!("npm install")
+  system("npm ls --depth=0 > /dev/null 2>&1") || system!("npm install")

Alternatively, checking for node_modules existence:

system!("npm install") unless Dir.exist?("node_modules")
demos/basic-v16-rspack-tanstack/db/seeds.rb (1)

1-9: Standard Rails seeds template – no issues.

This is the default Rails seeds.rb template with documentation comments. Since this demo app doesn't appear to require seed data, the file appropriately serves as a placeholder.

Optional: Remove the MovieGenre example

If you'd like to clean up the file, you could remove the generic MovieGenre example since it's not relevant to this TanStack Router demo:

 # This file should ensure the existence of records required to run the application in every environment (production,
 # development, test). The code here should be idempotent so that it can be executed at any point in every environment.
 # The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup).
-#
-# Example:
-#
-#   ["Action", "Comedy", "Drama", "Horror"].each do |genre_name|
-#     MovieGenre.find_or_create_by!(name: genre_name)
-#   end

However, keeping it is also perfectly fine for a demo application.

demos/basic-v16-rspack-tanstack/config/credentials.yml.enc (1)

1-1: LGTM! Standard Rails encrypted credentials file.

This encrypted credentials file follows Rails conventions and is safe to commit to version control. The actual secrets remain encrypted and can only be decrypted with the master key.

Optional: Consider adding a trailing newline to follow POSIX standards (most editors add this automatically).

Reminder: Ensure config/master.key is in .gitignore and never committed. For production, use the RAILS_MASTER_KEY environment variable.

demos/basic-v16-rspack-tanstack/app/assets/stylesheets/application.css (1)

1-10: Consider demonstrating CSS organization or clarifying the CSS strategy.

This manifest file contains helpful documentation but no actual CSS imports or rules. For a demo application, consider either:

  1. Adding example CSS organization — Show how to structure and import stylesheets (e.g., @import 'base.css'; or @import 'components/header.css';) to demonstrate the maintainability advice mentioned in the comment.

  2. Clarifying CSS handling strategy — Since the demo uses Rspack for bundling, clarify whether CSS should be managed through Rspack's asset pipeline, the Rails Propshaft pipeline, or a combination of both. This would help users understand the intended approach for styling in this stack.

demos/basic-v16-rspack-tanstack/app/javascript/packs/application.js (1)

1-1: Remove unnecessary ESLint directive.

The file contains no console usage, making this directive redundant.

♻️ Proposed fix
-/* eslint no-console:0 */
 // This file is automatically compiled by Webpack, along with any other files
 // present in this directory. You're encouraged to place your actual application logic in
 // a relevant structure within app/javascript and only use these pack files to reference
demos/basic-v16-rspack-tanstack/README.md (1)

1-24: Consider documenting the demo setup and features.

This is the default Rails README template. Given that this demo showcases TanStack Router with SSR, TypeScript, and Rspack, consider adding:

  • Prerequisites (Node.js, Ruby versions)
  • Setup commands (bundle install, yarn install, database setup)
  • How to run the dev server
  • Key features demonstrated (file-based routing, SSR, type-safe params)
  • How to run Playwright tests

Would you like me to draft a README based on the PR objectives and demo structure?

demos/basic-v16-rspack-tanstack/app/views/pwa/manifest.json.erb (1)

20-21: Consider using hex color codes for consistency.

Using named colors like "red" is valid, but hex codes (e.g., "#ff0000") are more precise and commonly used in PWA manifests. This is fine for a demo, but worth noting for production use.

demos/basic-v16-rspack-tanstack/app/javascript/src/HelloWorld/ror_components/HelloWorld.server.tsx (1)

1-5: Stale comment and potential leftover artifact.

The comment on line 3 references "React Router" but this demo uses TanStack Router. Additionally, since the PR objectives mention removing HelloWorld demo artifacts in favor of TanStack Router routes, consider whether this file should be retained or removed as part of the cleanup.

If this file is intentionally kept for reference, update the comment to reflect TanStack Router:

Suggested fix
 import HelloWorld from './HelloWorld.client';
-// This could be specialized for server rendering
-// For example, if using React Router, we'd have the SSR setup here.
+// This could be specialized for server rendering
+// For example, if using TanStack Router, we'd have the SSR setup here.

 export default HelloWorld;
demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js (1)

30-32: Consider distinguishing between "module not found" and other errors.

The current catch block treats all errors the same, which could mask genuine configuration issues (e.g., invalid plugin options) by logging them as "plugin not available."

Suggested improvement
     } catch (e) {
-      console.warn('TanStack Router plugin not available:', e.message);
+      if (e.code === 'MODULE_NOT_FOUND') {
+        console.warn('TanStack Router plugin not installed, skipping:', e.message);
+      } else {
+        console.error('TanStack Router plugin configuration error:', e);
+      }
     }
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/search.tsx (1)

4-7: Type mismatch: page is always defined after validation.

The SearchParams type declares page as optional (page?: number), but validateSearch always returns a number (defaulting to 1). Consider making the type non-optional to accurately reflect runtime behavior.

Suggested fix
 type SearchParams = {
   q?: string;
-  page?: number;
+  page: number;
 };

This would also simplify the component code by removing the (page || 1) fallbacks throughout.

demos/basic-v16-rspack-tanstack/config.ru (1)

5-6: Consider removing the load_server call.

The Rails.application.load_server call after run is non-standard. Typically, load_server is invoked internally by Rails during server boot (e.g., by Puma) and doesn't need to be called explicitly in config.ru. The standard pattern is simply:

require_relative "config/environment"

run Rails.application

If there's a specific reason this is needed (e.g., for a particular middleware setup), it should be called before run, not after.

Suggested fix
 require_relative "config/environment"

 run Rails.application
-Rails.application.load_server
demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js (1)

32-38: Redundant mini-css-extract-plugin removal.

This filtering logic is duplicated at lines 87-99, which performs the same removal along with additional CSS loader configuration. Consider removing this first pass to avoid redundancy.

♻️ Suggested removal
-  // Remove the mini-css-extract-plugin from the style loaders because
-  // the client build will handle exporting CSS.
-  // replace file-loader with null-loader
-  serverWebpackConfig.module.rules.forEach((loader) => {
-    if (loader.use && loader.use.filter) {
-      loader.use = loader.use.filter(
-        (item) => !(typeof item === 'string' && item.match(/mini-css-extract-plugin/))
-      );
-    }
-  });
demos/basic-v16-rspack-tanstack/babel.config.js (1)

11-18: Remove deprecated useBuiltIns option.

The useBuiltIns option in @babel/preset-react is deprecated and planned for removal in Babel 8. When using runtime: 'automatic', the automatic JSX runtime handles transformations without this option.

🔧 Suggested fix
       [
         '@babel/preset-react',
         {
           development: !isProductionEnv,
-          useBuiltIns: true,
           runtime: 'automatic',
         },
       ],
demos/basic-v16-rspack-tanstack/app/views/layouts/tanstack_app.html.erb (1)

2-2: Consider adding a lang attribute for accessibility.

Adding a language attribute helps screen readers and improves SEO.

Suggested change
-<html>
+<html lang="en">
demos/basic-v16-rspack-tanstack/app/javascript/src/TanStackApp/ror_components/TanStackApp.client.tsx (1)

5-11: Unused initialUrl prop.

The initialUrl prop is defined in TanStackAppProps but never used in the component. Per router.ts, the client uses browser history (not memory history), so the prop is likely intentionally unused—but leaving it in the interface is misleading.

Either remove the unused prop or add a comment explaining why it exists (e.g., for interface consistency with the server component).

Option 1: Remove unused prop
 interface TanStackAppProps {
-  initialUrl?: string;
 }
+
+type TanStackAppProps = Record<string, never>;

Or simply:

-interface TanStackAppProps {
-  initialUrl?: string;
-}
-
-const TanStackApp: React.FC<TanStackAppProps> = () => {
+const TanStackApp: React.FC = () => {
demos/basic-v16-rspack-tanstack/config/webpack/development.js (1)

4-4: Unused imports: devServer and inliningCss.

These imports from shakapacker are not used anywhere in this file. Consider removing them to keep the code clean.

Suggested fix
-const { devServer, inliningCss, config } = require('shakapacker');
+const { config } = require('shakapacker');
demos/basic-v16-rspack-tanstack/e2e/tanstack_router.spec.ts (2)

45-57: Consider verifying SPA navigation explicitly.

The comment states "Should navigate without full page reload" but there's no assertion verifying this. Consider adding a check to ensure client-side navigation occurred rather than a full reload.

Suggested enhancement
 test('navigation works after hydration', async ({ page }) => {
   await page.goto('/');

   // Wait for hydration
   await page.waitForLoadState('networkidle');

+  // Track if a navigation event (not a reload) occurs
+  const navigationPromise = page.waitForURL('/about', { waitUntil: 'domcontentloaded' });
+
   // Click navigation link
   await page.click('a[href="/about"]');

+  await navigationPromise;
+
   // Should navigate without full page reload
   await expect(page).toHaveURL('/about');
   await expect(page.locator('h1')).toContainText('About');
 });

Alternatively, you could capture page.on('load') events and assert none fired during navigation.


97-115: Good hydration consistency check, consider broader coverage.

The approach of capturing console warnings/errors and filtering for hydration-related messages is effective. Two suggestions for improvement:

  1. Consider testing multiple routes (especially parameterized routes like /users/123) to catch route-specific hydration mismatches.
  2. The string filter is case-sensitive; React's error messages typically use "Hydration" with capital H, which 'hydrat' would still match, but consider log.toLowerCase().includes(...) for robustness.
Suggested enhancement for broader coverage
test('no hydration mismatch warnings on key routes', async ({ page }) => {
  const routes = ['/', '/about', '/users/123', '/search?q=test'];
  
  for (const route of routes) {
    const consoleLogs: string[] = [];
    page.on('console', (msg) => {
      if (msg.type() === 'error' || msg.type() === 'warning') {
        consoleLogs.push(msg.text());
      }
    });

    await page.goto(route);
    await page.waitForLoadState('networkidle');

    const hydrationErrors = consoleLogs.filter(
      (log) => log.toLowerCase().includes('hydrat') || log.toLowerCase().includes('mismatch')
    );
    expect(hydrationErrors, `Hydration errors on ${route}`).toHaveLength(0);
  }
});
demos/basic-v16-rspack-tanstack/bin/switch-bundler (2)

64-87: Consider adding the new bundler's dependencies, not just removing the old ones.

The update_dependencies method removes the old bundler's dependencies from package.json but doesn't add the new bundler's dependencies to it. The subsequent install_dependencies method installs them via the package manager, but they won't appear in package.json with pinned versions since yarn add/npm install with package names (without versions) will add the latest versions.

This could lead to version inconsistencies. Consider either:

  1. Adding the new dependencies with explicit versions to package.json, or
  2. Documenting that versions will be resolved to latest at install time.

124-129: Consider detecting bun as a package manager.

The detection only covers yarn, pnpm, and npm. Bun is increasingly popular and uses bun.lockb as its lock file. This is optional but would improve compatibility.

✨ Optional: Add bun detection
   def detect_package_manager
+    return "bun" if File.exist?("bun.lockb")
     return "yarn" if File.exist?("yarn.lock")
     return "pnpm" if File.exist?("pnpm-lock.yaml")

     "npm"
   end

You'd also need to add bun cases in install_dependencies:

when "bun"
  system("bun", "add", *deps[:dependencies])
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 719d26b and a910ac9.

⛔ Files ignored due to path filters (4)
  • demos/basic-v16-rspack-tanstack/Gemfile.lock is excluded by !**/*.lock
  • demos/basic-v16-rspack-tanstack/package-lock.json is excluded by !**/package-lock.json
  • demos/basic-v16-rspack-tanstack/public/icon.png is excluded by !**/*.png
  • demos/basic-v16-rspack-tanstack/public/icon.svg is excluded by !**/*.svg
📒 Files selected for processing (96)
  • demos/basic-v16-rspack-tanstack/.demo-metadata.yml
  • demos/basic-v16-rspack-tanstack/.dev-services.yml.example
  • demos/basic-v16-rspack-tanstack/.gitignore
  • demos/basic-v16-rspack-tanstack/.rubocop.yml
  • demos/basic-v16-rspack-tanstack/Gemfile
  • demos/basic-v16-rspack-tanstack/Procfile.dev
  • demos/basic-v16-rspack-tanstack/Procfile.dev-prod-assets
  • demos/basic-v16-rspack-tanstack/Procfile.dev-static-assets
  • demos/basic-v16-rspack-tanstack/README.md
  • demos/basic-v16-rspack-tanstack/Rakefile
  • demos/basic-v16-rspack-tanstack/app/assets/stylesheets/application.css
  • demos/basic-v16-rspack-tanstack/app/controllers/application_controller.rb
  • demos/basic-v16-rspack-tanstack/app/controllers/tanstack_app_controller.rb
  • demos/basic-v16-rspack-tanstack/app/helpers/application_helper.rb
  • demos/basic-v16-rspack-tanstack/app/javascript/packs/application.js
  • demos/basic-v16-rspack-tanstack/app/javascript/packs/server-bundle.js
  • demos/basic-v16-rspack-tanstack/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/HelloWorld/ror_components/HelloWorld.module.css
  • demos/basic-v16-rspack-tanstack/app/javascript/src/HelloWorld/ror_components/HelloWorld.server.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/TanStackApp/ror_components/TanStackApp.client.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/TanStackApp/ror_components/TanStackApp.server.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/router.ts
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/__root.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/about.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/deep.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/index.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/index.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/search.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/$userId.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/index.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/types/css-modules.d.ts
  • demos/basic-v16-rspack-tanstack/app/jobs/application_job.rb
  • demos/basic-v16-rspack-tanstack/app/mailers/application_mailer.rb
  • demos/basic-v16-rspack-tanstack/app/models/application_record.rb
  • demos/basic-v16-rspack-tanstack/app/views/layouts/application.html.erb
  • demos/basic-v16-rspack-tanstack/app/views/layouts/mailer.html.erb
  • demos/basic-v16-rspack-tanstack/app/views/layouts/mailer.text.erb
  • demos/basic-v16-rspack-tanstack/app/views/layouts/tanstack_app.html.erb
  • demos/basic-v16-rspack-tanstack/app/views/pwa/manifest.json.erb
  • demos/basic-v16-rspack-tanstack/app/views/pwa/service-worker.js
  • demos/basic-v16-rspack-tanstack/app/views/tanstack_app/index.html.erb
  • demos/basic-v16-rspack-tanstack/babel.config.js
  • demos/basic-v16-rspack-tanstack/bin/brakeman
  • demos/basic-v16-rspack-tanstack/bin/bundle
  • demos/basic-v16-rspack-tanstack/bin/dev
  • demos/basic-v16-rspack-tanstack/bin/rails
  • demos/basic-v16-rspack-tanstack/bin/rake
  • demos/basic-v16-rspack-tanstack/bin/rubocop
  • demos/basic-v16-rspack-tanstack/bin/setup
  • demos/basic-v16-rspack-tanstack/bin/shakapacker
  • demos/basic-v16-rspack-tanstack/bin/shakapacker-config
  • demos/basic-v16-rspack-tanstack/bin/shakapacker-dev-server
  • demos/basic-v16-rspack-tanstack/bin/shakapacker-precompile-hook
  • demos/basic-v16-rspack-tanstack/bin/switch-bundler
  • demos/basic-v16-rspack-tanstack/bin/thrust
  • demos/basic-v16-rspack-tanstack/config.ru
  • demos/basic-v16-rspack-tanstack/config/application.rb
  • demos/basic-v16-rspack-tanstack/config/boot.rb
  • demos/basic-v16-rspack-tanstack/config/credentials.yml.enc
  • demos/basic-v16-rspack-tanstack/config/database.yml
  • demos/basic-v16-rspack-tanstack/config/environment.rb
  • demos/basic-v16-rspack-tanstack/config/environments/development.rb
  • demos/basic-v16-rspack-tanstack/config/environments/production.rb
  • demos/basic-v16-rspack-tanstack/config/environments/test.rb
  • demos/basic-v16-rspack-tanstack/config/initializers/assets.rb
  • demos/basic-v16-rspack-tanstack/config/initializers/content_security_policy.rb
  • demos/basic-v16-rspack-tanstack/config/initializers/filter_parameter_logging.rb
  • demos/basic-v16-rspack-tanstack/config/initializers/inflections.rb
  • demos/basic-v16-rspack-tanstack/config/initializers/react_on_rails.rb
  • demos/basic-v16-rspack-tanstack/config/locales/en.yml
  • demos/basic-v16-rspack-tanstack/config/puma.rb
  • demos/basic-v16-rspack-tanstack/config/routes.rb
  • demos/basic-v16-rspack-tanstack/config/shakapacker.yml
  • demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js
  • demos/basic-v16-rspack-tanstack/config/webpack/commonWebpackConfig.js
  • demos/basic-v16-rspack-tanstack/config/webpack/development.js
  • demos/basic-v16-rspack-tanstack/config/webpack/generateWebpackConfigs.js
  • demos/basic-v16-rspack-tanstack/config/webpack/production.js
  • demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js
  • demos/basic-v16-rspack-tanstack/config/webpack/test.js
  • demos/basic-v16-rspack-tanstack/config/webpack/webpack.config.js
  • demos/basic-v16-rspack-tanstack/db/seeds.rb
  • demos/basic-v16-rspack-tanstack/e2e/tanstack_router.spec.ts
  • demos/basic-v16-rspack-tanstack/package.json
  • demos/basic-v16-rspack-tanstack/playwright.config.ts
  • demos/basic-v16-rspack-tanstack/public/400.html
  • demos/basic-v16-rspack-tanstack/public/404.html
  • demos/basic-v16-rspack-tanstack/public/406-unsupported-browser.html
  • demos/basic-v16-rspack-tanstack/public/422.html
  • demos/basic-v16-rspack-tanstack/public/500.html
  • demos/basic-v16-rspack-tanstack/public/robots.txt
  • demos/basic-v16-rspack-tanstack/ssr-generated/manifest.json
  • demos/basic-v16-rspack-tanstack/ssr-generated/server-bundle.js
  • demos/basic-v16-rspack-tanstack/test/test_helper.rb
  • demos/basic-v16-rspack-tanstack/tsconfig.json
🧰 Additional context used
🧬 Code graph analysis (23)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/index.tsx (2)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/deep.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/search.tsx (8)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/__root.tsx (1)
  • Route (4-6)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/about.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/deep.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/$userId.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/deep.tsx (3)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/__root.tsx (1)
  • Route (4-6)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/TanStackApp/ror_components/TanStackApp.client.tsx (1)
demos/basic-v16-rspack-tanstack/app/javascript/src/router.ts (1)
  • createRouter (11-29)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/$userId.tsx (8)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/__root.tsx (1)
  • Route (4-6)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/about.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/deep.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/search.tsx (1)
  • Route (9-22)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js (2)
demos/basic-v16-rspack-tanstack/config/webpack/commonWebpackConfig.js (2)
  • require (5-5)
  • commonWebpackConfig (16-16)
demos/basic-v16-rspack-tanstack/config/webpack/generateWebpackConfigs.js (1)
  • serverWebpackConfig (5-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/index.tsx (8)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/__root.tsx (1)
  • Route (4-6)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/about.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/deep.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/search.tsx (1)
  • Route (9-22)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/$userId.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/config/webpack/commonWebpackConfig.js (3)
demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js (3)
  • require (6-6)
  • require (20-20)
  • commonWebpackConfig (5-5)
demos/basic-v16-rspack-tanstack/config/webpack/development.js (1)
  • require (4-4)
demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js (2)
  • require (4-4)
  • commonWebpackConfig (5-5)
demos/basic-v16-rspack-tanstack/config/webpack/production.js (4)
demos/basic-v16-rspack-tanstack/config/webpack/development.js (2)
  • generateWebpackConfigs (6-6)
  • require (4-4)
demos/basic-v16-rspack-tanstack/config/webpack/test.js (1)
  • generateWebpackConfigs (4-4)
demos/basic-v16-rspack-tanstack/config/webpack/commonWebpackConfig.js (1)
  • require (5-5)
demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js (1)
  • require (4-4)
demos/basic-v16-rspack-tanstack/config/environments/development.rb (1)
demos/basic-v16-rspack-tanstack/config/application.rb (1)
  • config (22-41)
demos/basic-v16-rspack-tanstack/config/environments/test.rb (1)
demos/basic-v16-rspack-tanstack/config/application.rb (1)
  • config (22-41)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/__root.tsx (8)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/about.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/deep.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/search.tsx (1)
  • Route (9-22)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/$userId.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/TanStackApp/ror_components/TanStackApp.server.tsx (1)
demos/basic-v16-rspack-tanstack/app/javascript/src/router.ts (1)
  • createRouter (11-29)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/index.tsx (3)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/__root.tsx (1)
  • Route (4-6)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/$userId.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/config/initializers/filter_parameter_logging.rb (1)
demos/basic-v16-rspack-tanstack/config/application.rb (1)
  • config (22-41)
demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js (6)
demos/basic-v16-rspack-tanstack/config/webpack/webpack.config.js (4)
  • path (6-6)
  • require (1-1)
  • require (2-2)
  • require (3-3)
demos/basic-v16-rspack-tanstack/config/webpack/commonWebpackConfig.js (2)
  • require (5-5)
  • commonWebpackConfig (16-16)
demos/basic-v16-rspack-tanstack/config/webpack/development.js (1)
  • require (4-4)
demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js (2)
  • require (4-4)
  • commonWebpackConfig (5-5)
demos/basic-v16-rspack-tanstack/config/webpack/generateWebpackConfigs.js (1)
  • clientConfig (8-8)
demos/basic-v16-rspack-tanstack/config/application.rb (1)
  • config (22-41)
demos/basic-v16-rspack-tanstack/config/initializers/assets.rb (1)
demos/basic-v16-rspack-tanstack/config/application.rb (1)
  • config (22-41)
demos/basic-v16-rspack-tanstack/babel.config.js (5)
demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js (2)
  • require (6-6)
  • require (20-20)
demos/basic-v16-rspack-tanstack/config/webpack/commonWebpackConfig.js (1)
  • require (5-5)
demos/basic-v16-rspack-tanstack/config/webpack/development.js (1)
  • require (4-4)
demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js (1)
  • require (4-4)
demos/basic-v16-rspack-tanstack/config/webpack/webpack.config.js (3)
  • require (1-1)
  • require (2-2)
  • require (3-3)
demos/basic-v16-rspack-tanstack/config/webpack/generateWebpackConfigs.js (5)
demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js (3)
  • require (6-6)
  • require (20-20)
  • clientConfig (9-9)
demos/basic-v16-rspack-tanstack/config/webpack/commonWebpackConfig.js (1)
  • require (5-5)
demos/basic-v16-rspack-tanstack/config/webpack/development.js (1)
  • require (4-4)
demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js (2)
  • require (4-4)
  • serverWebpackConfig (14-14)
demos/basic-v16-rspack-tanstack/config/webpack/webpack.config.js (3)
  • require (1-1)
  • require (2-2)
  • require (3-3)
demos/basic-v16-rspack-tanstack/config/routes.rb (1)
packages/shakacode_demo_common/lib/shakacode_demo_common.rb (1)
  • root (10-12)
demos/basic-v16-rspack-tanstack/config/environments/production.rb (1)
demos/basic-v16-rspack-tanstack/config/application.rb (1)
  • config (22-41)
demos/basic-v16-rspack-tanstack/config/webpack/webpack.config.js (4)
demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js (2)
  • require (6-6)
  • require (20-20)
demos/basic-v16-rspack-tanstack/config/webpack/commonWebpackConfig.js (1)
  • require (5-5)
demos/basic-v16-rspack-tanstack/config/webpack/development.js (1)
  • require (4-4)
demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js (1)
  • require (4-4)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/about.tsx (8)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/__root.tsx (1)
  • Route (4-6)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/deep.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/search.tsx (1)
  • Route (9-22)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/$userId.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/index.tsx (1)
  • Route (3-5)
🪛 Biome (2.1.2)
demos/basic-v16-rspack-tanstack/ssr-generated/server-bundle.js

[error] 3-4: eval() exposes to security risks and performance issues.

See the MDN web docs for more details.
Refactor the code so that it doesn't need to call eval().

(lint/security/noGlobalEval)

🪛 Brakeman (7.1.1)
demos/basic-v16-rspack-tanstack/app/controllers/application_controller.rb

[high] 1-1: protect_from_forgery should be called in ApplicationController
Type: Cross-Site Request Forgery
Confidence: High
More info: https://brakemanscanner.org/docs/warning_types/cross-site_request_forgery/

(Cross-Site Request Forgery)

🪛 Checkov (3.2.334)
demos/basic-v16-rspack-tanstack/config/database.yml

[medium] 68-69: Basic Auth Credentials

(CKV_SECRET_4)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: claude-review

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +9 to +17
const TanStackApp: React.FC<TanStackAppProps> = ({ initialUrl }) => {
// Create router with memory history for SSR
const router = createRouter({ initialUrl });

// Load the router to ensure route data is ready
router.load();

return <RouterProvider router={router} />;
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n demos/basic-v16-rspack-tanstack/app/javascript/src/TanStackApp/ror_components/TanStackApp.server.tsx

Repository: shakacode/react_on_rails-demos

Length of output: 706


🌐 Web query:

TanStack Router SSR server-side rendering router.load async await

💡 Result:

Short answer: call router.load() (await it) on the server before rendering. Use the sync option to wait for all loaders to finish (or use router.invalidate() to force reload). Then render with the SSR helpers (defaultRenderHandler / renderRouterToString / RouterServer).

Minimal example:

  • create router (shared)
  • in server entry:
    const router = createRouter()
    // make router match the request URL (your router setup will do this)
    await router.load({ sync: true }) // waits for route loaders to finish
    const html = renderRouterToString(...)
    res.end(html)

Notes:

  • router.load() respects route.staleTime and will not force reload fresh data; use router.invalidate() to force reload. [1]
  • Use the TanStack Router SSR helpers (defaultRenderHandler or renderRouterToString / RouterServer) to serialize/dehydrate loader data for the client. For deferred/streaming data use the deferred/Await pattern and the streaming SSR guide. [2][3]

Sources:
[1] Router API — .load() details.
[2] TanStack Router SSR guide (non-streaming & SSR helpers).
[3] Deferred data / streaming SSR (defer + ).


🏁 Script executed:

fd -e "tsx\|ts" -path "*/server*" demos/basic-v16-rspack-tanstack/app/javascript/src

Repository: shakacode/react_on_rails-demos

Length of output: 242


🏁 Script executed:

rg "RouterServer|renderToString" demos/basic-v16-rspack-tanstack/app/javascript/src --type ts --type tsx -B 2 -A 2

Repository: shakacode/react_on_rails-demos

Length of output: 101


🏁 Script executed:

find demos/basic-v16-rspack-tanstack -name "*entry*" -o -name "*server*" | grep -E "\.(tsx?|jsx?)$" | head -20

Repository: shakacode/react_on_rails-demos

Length of output: 476


🏁 Script executed:

cat -n demos/basic-v16-rspack-tanstack/app/javascript/packs/server-bundle.js

Repository: shakacode/react_on_rails-demos

Length of output: 184


🏁 Script executed:

rg "TanStackApp\|router\.load" demos/basic-v16-rspack-tanstack --type js --type jsx -B 3 -A 3

Repository: shakacode/react_on_rails-demos

Length of output: 101


🏁 Script executed:

rg "TanStackApp" demos/basic-v16-rspack-tanstack --type ts -B 2 -A 2

Repository: shakacode/react_on_rails-demos

Length of output: 3086


🏁 Script executed:

rg "TanStackApp" demos/basic-v16-rspack-tanstack -B 2 -A 2

Repository: shakacode/react_on_rails-demos

Length of output: 3238


🏁 Script executed:

find demos/basic-v16-rspack-tanstack -name "*.erb" -o -name "*.html" | xargs grep -l "TanStackApp" 2>/dev/null | head -5

Repository: shakacode/react_on_rails-demos

Length of output: 143


🏁 Script executed:

cat -n demos/basic-v16-rspack-tanstack/app/javascript/src/router.ts

Repository: shakacode/react_on_rails-demos

Length of output: 1173


🏁 Script executed:

rg "async\|Promise" demos/basic-v16-rspack-tanstack/app/javascript/src/TanStackApp --type ts -B 1 -A 1

Repository: shakacode/react_on_rails-demos

Length of output: 56


Await router.load() before rendering to ensure route data is available during SSR.

router.load() returns a Promise but the return value is not awaited, which means the component renders before route loaders complete. Per TanStack Router's SSR guidelines, you must await router.load({ sync: true }) before rendering to ensure route data is ready.

Since this is a synchronous function component, you'll need to restructure the component to be async or move the router loading to a wrapper component that can handle the Promise. Consider:

  1. Convert TanStackApp to an async Server Component that awaits router.load() before returning JSX
  2. Move router creation and loading to the server entry point that calls renderToString, then pass the preloaded router to the component
🤖 Prompt for AI Agents
In
`@demos/basic-v16-rspack-tanstack/app/javascript/src/TanStackApp/ror_components/TanStackApp.server.tsx`
around lines 9 - 17, The component calls router.load() but doesn't await it, so
route loaders may not finish before render; change the code to await
router.load({ sync: true }) before returning JSX by making TanStackApp an async
Server Component (or move router creation/loading to the server entry that calls
renderToString) — specifically, ensure createRouter(...) is followed by await
router.load({ sync: true }) (or perform that load in your wrapper) and only then
return <RouterProvider router={router} /> so the RouterProvider receives a fully
preloaded router.

Comment on lines +1 to +13
{
"server-bundle.js": "/packs/server-bundle.js",
"entrypoints": {
"server-bundle": {
"assets": {
"js": [
"/packs/server-bundle.js"
],
"css": []
}
}
}
} No newline at end of file
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

# Check the gitignore at the demo level
echo "=== Checking demos/basic-v16-rspack-tanstack/.gitignore ==="
if [ -f "demos/basic-v16-rspack-tanstack/.gitignore" ]; then
  cat -n "demos/basic-v16-rspack-tanstack/.gitignore"
else
  echo "No .gitignore in demo directory"
fi

# Check root level .gitignore
echo -e "\n=== Checking root .gitignore for ssr-generated ==="
rg -n "ssr-generated" .gitignore 2>/dev/null || echo "ssr-generated not found in root .gitignore"

# Check if the file actually exists
echo -e "\n=== Checking if manifest.json exists ==="
ls -la "demos/basic-v16-rspack-tanstack/ssr-generated/" 2>/dev/null || echo "Directory not found"

# Check for README in demo
echo -e "\n=== Checking for README in demo ==="
find "demos/basic-v16-rspack-tanstack" -maxdepth 1 -name "README*" -o -name "readme*" | head -5

Repository: shakacode/react_on_rails-demos

Length of output: 1211


🏁 Script executed:

#!/bin/bash

# Check the README for context about ssr-generated
echo "=== Checking demos/basic-v16-rspack-tanstack/README.md ==="
cat "demos/basic-v16-rspack-tanstack/README.md"

Repository: shakacode/react_on_rails-demos

Length of output: 506


🏁 Script executed:

#!/bin/bash

# Check git history for the manifest.json
echo "=== Git history for manifest.json ==="
git log --oneline --follow "demos/basic-v16-rspack-tanstack/ssr-generated/manifest.json" | head -10

# Check git history for .gitignore
echo -e "\n=== Git history for .gitignore (recent commits) ==="
git log --oneline -p "demos/basic-v16-rspack-tanstack/.gitignore" | head -40

# Check if manifest.json is currently tracked
echo -e "\n=== Git status of manifest.json ==="
git status "demos/basic-v16-rspack-tanstack/ssr-generated/manifest.json" 2>&1 || echo "File status check failed"

# Check if it's in git index
echo -e "\n=== Check if file is actually tracked ==="
git ls-files "demos/basic-v16-rspack-tanstack/ssr-generated/manifest.json"

Repository: shakacode/react_on_rails-demos

Length of output: 1151


Remove /ssr-generated from .gitignore or stop tracking the file with git rm --cached.

manifest.json is currently tracked in git but /ssr-generated is listed in .gitignore. This creates a conflict: the .gitignore rule only prevents new files from being added, not removal of already-tracked files. The directory will continue to be tracked despite the rule.

If the file is intentionally committed for demo purposes (to allow running without a build step), remove the /ssr-generated rule from .gitignore. Otherwise, use git rm --cached demos/basic-v16-rspack-tanstack/ssr-generated/ to stop tracking it while keeping the files locally.

🤖 Prompt for AI Agents
In `@demos/basic-v16-rspack-tanstack/ssr-generated/manifest.json` around lines 1 -
13, manifest.json inside the ssr-generated output is currently tracked despite a
`/ssr-generated` rule in .gitignore; either remove the `/ssr-generated` entry
from .gitignore so the committed manifest.json stays intentionally, or stop
tracking the generated directory (untrack ssr-generated with git rm --cached and
commit) so manifest.json is no longer tracked while keeping the files locally.

Comment on lines +1 to +45
(function() {
var __webpack_modules__ = ({
"./app/javascript/packs/server-bundle.js": (function () {
eval("{// Placeholder comment - auto-generated imports will be prepended here by react_on_rails:generate_packs\n\n\n//# sourceURL=webpack://app/./app/javascript/packs/server-bundle.js?395a\n}");

}),

});
// The module cache
var __webpack_module_cache__ = {};

// The require function
function __webpack_require__(moduleId) {

// Check if module is in cache
var cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
// Create a new module (and put it into the cache)
var module = (__webpack_module_cache__[moduleId] = {
exports: {}
});
// Execute the module function
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);

// Return the exports of the module
return module.exports;

}

// webpack/runtime/rspack_version
!function() {
__webpack_require__.rv = function() { return "1.7.2"; }
}();
// webpack/runtime/rspack_unique_id
!function() {
__webpack_require__.ruid = "bundler=rspack@1.7.2";
}();
// startup
// Load entry module and return exports
// This entry module can't be inlined because the eval devtool is used.
var __webpack_exports__ = __webpack_require__("./app/javascript/packs/server-bundle.js");
})()
; No newline at end of file
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if ssr-generated is in .gitignore
echo "=== Checking .gitignore files in repository ==="
fd -t f '.gitignore' --max-depth 3 | head -20
echo ""

# Check content of .gitignore in repo root and demos
if [ -f ".gitignore" ]; then
  echo "=== Root .gitignore content ==="
  cat .gitignore
  echo ""
fi

# Check if ssr-generated is mentioned in any .gitignore
echo "=== Searching for 'ssr-generated' in .gitignore files ==="
rg 'ssr-generated' --type ignore --max-count 10
echo ""

# Check if other demos have ssr-generated directories
echo "=== Checking for ssr-generated directories in demos ==="
fd -t d 'ssr-generated' demos/
echo ""

# List structure of the target demo
echo "=== Structure of target demo directory ==="
fd -t d '' demos/basic-v16-rspack-tanstack --max-depth 2

Repository: shakacode/react_on_rails-demos

Length of output: 2273


🏁 Script executed:

#!/bin/bash
# Check if server-bundle.js is tracked by git
echo "=== Checking if server-bundle.js is tracked by git ==="
git ls-files | grep -i 'ssr-generated' || echo "Not found in git tracked files"
echo ""

# Check git status for ssr-generated
echo "=== Git status for ssr-generated paths ==="
git status --porcelain | grep -i 'ssr-generated' || echo "No ssr-generated in git status"
echo ""

# Search .gitignore more carefully with plain grep
echo "=== Searching .gitignore for build/bundle patterns ==="
cat .gitignore | grep -i -E '(public|packs|bundle|generated|build|dist)' || echo "No matching patterns"
echo ""

# Check what files exist in the target directory
echo "=== Checking if ssr-generated directory exists in demo ==="
ls -la demos/basic-v16-rspack-tanstack/ | grep -i generated || echo "No generated directory found via ls"
echo ""

# Try to find any file with 'server-bundle' name
echo "=== Searching for server-bundle files ==="
fd 'server-bundle' demos/basic-v16-rspack-tanstack 2>/dev/null || echo "No server-bundle files found"

Repository: shakacode/react_on_rails-demos

Length of output: 776


🏁 Script executed:

#!/bin/bash
# Check for README or documentation in the demo
echo "=== Looking for documentation in demo directory ==="
fd -t f -e md -e txt -e markdown demos/basic-v16-rspack-tanstack | head -20
echo ""

# Check package.json for build scripts
echo "=== Checking package.json for build scripts ==="
if [ -f "demos/basic-v16-rspack-tanstack/package.json" ]; then
  cat demos/basic-v16-rspack-tanstack/package.json | grep -A 20 '"scripts"'
fi
echo ""

# Check if .gitignore excludes anything related to builds or generated
echo "=== Full .gitignore content again for context ==="
cat .gitignore | head -40
echo ""

# Check other demos to see if they have similar ssr-generated files
echo "=== Checking other demos for ssr-generated pattern ==="
fd -t d 'ssr-generated' . 2>/dev/null || echo "No other ssr-generated found"
echo ""

# Look for any comments or documentation about generated files
echo "=== Searching for docs about bundle generation ==="
rg -l 'ssr-generated|server-bundle' demos/ --type md -m 3 2>/dev/null || echo "No markdown docs found"

Repository: shakacode/react_on_rails-demos

Length of output: 1260


🏁 Script executed:

#!/bin/bash
# Check if package.json exists and show scripts section
echo "=== Package.json scripts section ==="
if [ -f "demos/basic-v16-rspack-tanstack/package.json" ]; then
  python3 << 'EOF'
import json
with open("demos/basic-v16-rspack-tanstack/package.json") as f:
    data = json.load(f)
    if "scripts" in data:
        print("Scripts found:")
        for key, val in data["scripts"].items():
            print(f"  {key}: {val}")
    else:
        print("No scripts section found")
EOF
else
  echo "No package.json found in demo directory"
fi
echo ""

# Check the rspack migration doc for context
echo "=== Rspack migration doc (checking for ssr-generated references) ==="
if [ -f "demos/basic-v16-rspack/docs/webpack-to-rspack-migration.md" ]; then
  rg 'ssr-generated|server-bundle|build.*artifact' demos/basic-v16-rspack/docs/webpack-to-rspack-migration.md -A 2 -B 2 || echo "No relevant references found"
else
  echo "Migration doc not accessible"
fi
echo ""

# Final check: look at git history for when ssr-generated was added
echo "=== Git log for ssr-generated files (last 3 commits) ==="
git log --oneline -n 20 -- "demos/basic-v16-rspack-tanstack/ssr-generated/" 2>/dev/null || echo "Unable to retrieve git history"

Repository: shakacode/react_on_rails-demos

Length of output: 480


Committed build artifact — add to .gitignore.

The ssr-generated/ directory and its files (server-bundle.js, manifest.json) are tracked by git but not excluded in .gitignore. This appears unintentional—the rspack migration documentation explicitly treats ssr-generated as a cleanup artifact (listing rm -rf ssr-generated alongside other temporary build outputs), yet it's committed here.

This creates several problems:

  • Files may become stale if source files change but the bundle isn't rebuilt
  • Unnecessary repository bloat
  • Risk of merge conflicts on generated code

Add ssr-generated/ to .gitignore and document the build command needed to regenerate these files for deployment.

🧰 Tools
🪛 Biome (2.1.2)

[error] 3-4: eval() exposes to security risks and performance issues.

See the MDN web docs for more details.
Refactor the code so that it doesn't need to call eval().

(lint/security/noGlobalEval)

🤖 Prompt for AI Agents
In `@demos/basic-v16-rspack-tanstack/ssr-generated/server-bundle.js` around lines
1 - 45, The repository currently commits generated SSR assets (ssr-generated/
including server-bundle.js and manifest.json from react_on_rails:generate_packs)
which should be ignored; add "ssr-generated/" to .gitignore and remove the
tracked files from Git (git rm --cached) so they are no longer committed, and
update the project README or deploy docs to include the exact build command or
npm/yarn script used to regenerate these files (the project's SSR/webpack/rspack
build script that produces ssr-generated) so maintainers know how to recreate
server-bundle.js and manifest.json for deployments.

@claude
Copy link

claude bot commented Jan 14, 2026

PR Review: TanStack Router Demo with SSR and TypeScript

Summary

This PR adds a comprehensive new demo showcasing TanStack Router integration with React on Rails, featuring SSR, TypeScript, and Rspack. The implementation is well-structured and demonstrates best practices for modern React routing with Rails.

✅ Strengths

1. Excellent Documentation

  • Clear README with setup instructions, build commands, and deployment guidance
  • Well-documented SSR limitations and workarounds (synchronous rendering constraint)
  • Helpful inline comments explaining critical decisions (e.g., void router.load() rationale)
  • Updated CLAUDE.md with mise/asdf version manager guidance

2. Solid Architecture

  • Clean separation between client and server components (.client.tsx / .server.tsx)
  • Proper SSR implementation using memory history on server, browser history on client
  • Correct handling of hydration with initialUrl prop propagation
  • Environment-specific webpack configs following existing demo patterns

3. Comprehensive Testing

  • Excellent Playwright E2E test coverage (130 lines)
  • Tests verify SSR, client hydration, navigation, and hydration consistency
  • Smart use of domcontentloaded instead of networkidle for HMR compatibility
  • Hydration mismatch detection test is particularly valuable

4. TypeScript Quality

  • Proper type registration for TanStack Router
  • Clean interfaces (TanStackAppProps, RouterContext)
  • Type-safe route definitions using TanStack's file-based routing

5. Build Configuration

  • Rspack with React Fast Refresh properly configured
  • Correct separation of client/server bundles
  • private_output_path: ssr-generated ensures SSR bundle security
  • enforce_private_server_bundles: true prevents exposure of server code

🔍 Code Quality Observations

Good Practices

  • ✅ CSRF protection enabled in controller
  • ✅ Routes explicitly defined in config/routes.rb for SSR support
  • ✅ Proper use of request.fullpath for initial URL
  • ✅ Mock data structures demonstrate param handling without external dependencies
  • .gitignore correctly excludes generated files (routeTree.gen.ts, ssr-generated/)

Security Considerations

  • ✅ No sensitive data exposure in demo code
  • ✅ SSR bundles properly isolated in private directory
  • ✅ CSRF meta tags included in layout
  • ✅ CSP meta tag present for content security policy support
  • ⚠️ Note: allow_browser versions: :modern in ApplicationController is Rails 8's automatic modern browser enforcement - ensure this aligns with target browser support

Performance

  • ✅ Rspack provides faster build times vs webpack
  • ✅ React Fast Refresh enabled for optimal DX
  • defaultPreload: 'intent' enables smart route prefetching
  • scrollRestoration: true provides better UX
  • ✅ Production config uses content hashing for cache busting

🎯 Suggestions for Improvement

Minor Issues

  1. Route Maintenance (Low Priority)

    # config/routes.rb
    get "about", to: "tanstack_app#index"
    get "search", to: "tanstack_app#index"
    get "users", to: "tanstack_app#index"
    get "users/:userId", to: "tanstack_app#index"

    Issue: Each new route requires manual Rails route definition. For a production app, consider:

    • Catch-all route: get '*path', to: 'tanstack_app#index' (be mindful of API routes)
    • Document route maintenance requirements in README

    For this demo: Current approach is actually better because it explicitly shows which routes support SSR, which is valuable for learning purposes.

  2. Error Handling

    • No error boundaries in React components
    • Consider adding a root error boundary in __root.tsx to demonstrate error handling patterns
    • Not blocking, but would enhance demo completeness
  3. Accessibility

    • Navigation links lack ARIA labels
    • Consider adding aria-current="page" to active nav links
    • Would make demo more production-ready as a reference

Documentation Enhancements

  1. Add to README:

    ## Known Limitations
    - Manual route definition required for each SSR route
    - Async route loaders not supported (synchronous SSR only)
    - TanStack Router DevTools only available in development
  2. SEO Considerations:

    • Document that SSR enables SEO-friendly rendering
    • Add example meta tags to demonstrate dynamic title/description support

🔧 Repository-Level Changes

bin/mise-exec

  • ✅ Well-documented wrapper for mise/asdf compatibility
  • ✅ Proper error handling with helpful install instructions
  • ✅ Correctly excluded from RuboCop (shell script)

.rubocop.yml

  • ✅ Simplified exclusion pattern: demos/**/* (each demo has its own config)
  • ✅ Cleaner than previous per-directory exclusions

CLAUDE.md Updates

  • ✅ Clear documentation of Conductor/mise integration issue
  • ✅ Practical examples of bin/mise-exec usage
  • ✅ Links to related issue for context

🧪 Test Plan Verification

Based on the PR test plan, here's what's covered:

  • ✅ E2E tests verify: SSR, hydration, navigation, URL params, search params, nested routes
  • ✅ RuboCop configuration updated
  • ⚠️ Manual verification needed: Build passes, tests pass (not shown in PR)

🚀 Deployment Readiness

Production Considerations:

  • ✅ Asset precompilation properly configured
  • ✅ SSR bundle path correctly set
  • ✅ Cache manifests enabled in production
  • ✅ Content hashing enabled for cache busting
  • ⚠️ Ensure deployment platform runs RAILS_ENV=production bin/rails assets:precompile
  • ⚠️ Verify ssr-generated/ directory is created during deployment (not in git)

📊 Overall Assessment

Rating: ⭐⭐⭐⭐⭐ (Excellent)

This is a high-quality demo that:

  • Demonstrates advanced React on Rails patterns effectively
  • Provides excellent learning material for TanStack Router + SSR integration
  • Follows project conventions and best practices
  • Includes comprehensive testing
  • Has clear, helpful documentation

Recommendation: ✅ Approve with minor suggestions

The suggestions above are non-blocking and mostly about enhancing an already strong implementation. The core functionality is solid, well-tested, and production-ready as a demo.

🎓 Learning Value

This demo successfully teaches:

  1. How to integrate TanStack Router with React on Rails
  2. SSR implementation patterns and limitations
  3. File-based routing in a Rails context
  4. Proper separation of client/server bundles
  5. Type-safe routing with TypeScript
  6. E2E testing strategies for SSR + hydration

Great work! This will be a valuable reference for the community. 🚀

@claude
Copy link

claude bot commented Jan 14, 2026

PR Review: TanStack Router Demo with SSR and TypeScript

I've completed a thorough review of this PR. Overall, this is an excellent addition to the demo repository that showcases a modern, well-architected integration of TanStack Router with React on Rails SSR. The implementation is solid, well-documented, and follows best practices.

✅ Strengths

1. Excellent Architecture & Code Quality

  • SSR Implementation: The server/client component split is clean and well-documented. The synchronous SSR limitation is clearly documented in TanStackApp.server.tsx:10-21 with helpful guidance on alternatives.
  • Type Safety: Proper TypeScript usage throughout with correct module augmentation for TanStack Router (router.ts:31-36).
  • Router Configuration: Smart use of memory history for SSR and browser history for client in router.ts:12-26.
  • Component Organization: Follows React on Rails conventions with ror_components subdirectory structure.

2. Comprehensive Documentation

  • The README is thorough with clear sections on setup, building, configuration, and deployment.
  • SSR limitations are well-documented with actionable alternatives.
  • CLAUDE.md additions provide valuable context for version managers (mise/asdf) - this will help future contributors.

3. Strong Test Coverage

  • Playwright E2E tests (e2e/tanstack_router.spec.ts) cover both SSR and hydration verification.
  • Tests verify no hydration mismatches (lines 106-129) - critical for SSR correctness.
  • Good coverage of dynamic routes, search params, and nested routes.

4. Webpack/Rspack Configuration

  • serverWebpackConfig.js is well-structured with helpful comments.
  • Proper use of Shakapacker 9.0+ privateOutputPath with fallback (lines 50-72).
  • Correct handling of CSS modules for SSR (exportOnlyLocals: true on line 110).
  • Good validation and warning messages for configuration issues (lines 65-72).

5. Rails Integration

  • Clean controller passing initialUrl for proper SSR routing (tanstack_app_controller.rb:8-10).
  • Proper React on Rails configuration with security best practices (enforce_private_server_bundles: true).

🔍 Observations & Suggestions

1. Potential Hydration Issue in TanStackApp.server.tsx ⚠️

File: app/javascript/src/TanStackApp/ror_components/TanStackApp.server.tsx:31

void router.load();

The void operator here discards the Promise returned by router.load(), but the load hasn't completed when RouterProvider renders. This works for synchronous loaders, but the comment on line 29-30 says "for synchronous loaders the route matching happens immediately" which may not be entirely accurate.

Suggestion: Consider adding a comment clarifying that route matching is synchronous even though load() returns a Promise, or verify with TanStack Router docs that this pattern is officially supported for SSR. The current implementation appears to work based on tests, but the semantics could be clearer.

2. Client Component Hook Usage

File: app/javascript/src/TanStackApp/ror_components/TanStackApp.client.tsx:11

const [router] = React.useState(() => createRouter({ initialUrl }));

This is correct and follows React best practices (lazy initialization with function). However, you might want to verify that TanStack Router doesn't provide a recommended useRouter hook or similar for this use case in their SSR docs.

3. Security: No Input Validation

File: app/controllers/tanstack_app_controller.rb:9

initialUrl: request.fullpath

While this is a demo app, request.fullpath includes user-controlled query parameters that are passed directly to React. This is generally safe since React escapes content, but in production you might want to:

  • Document this trust boundary
  • Add a note about validation for production use

Impact: Low for a demo, but worth a comment for educational purposes.

4. CSS Modules Type Definition

File: app/javascript/types/css-modules.d.ts

The CSS modules type definition is good, but it declares all CSS files as modules. If you use both CSS modules and regular CSS, you might want to be more specific with the pattern (e.g., *.module.css).

5. Test Timeout Waiting

File: e2e/tanstack_router.spec.ts:121

await page.waitForTimeout(1000);

Hard timeouts in tests can be flaky. Consider using Playwright's page.waitForFunction() or other condition-based waits if possible, though I understand this may be necessary for hydration checks.

6. bin/mise-exec Portability

File: bin/mise-exec:1

#!/usr/bin/env zsh

The script is hardcoded to zsh. Consider using #!/usr/bin/env sh or #!/usr/bin/env bash for broader compatibility, unless zsh-specific features are required (they don't appear to be).

📋 Minor Nitpicks

  1. Rubocop Exclude Pattern: The change to .rubocop.yml:12 (demos/**/*) is quite broad. Consider if you want to exclude all demo content from root linting or if you meant just demos/**/tmp/**/* and similar directories.

  2. Generated Files: The .gitignore correctly excludes generated files, which is good. Ensure the README or a contributing guide mentions these get regenerated on build.

  3. Mock Data: The mock user data in users/$userId.tsx:8-35 is fine for a demo but could use a comment indicating it's for demonstration only.

🎯 Performance Considerations

  • Rspack: Good choice for build speed. The configuration looks optimal.
  • Bundle Splitting: Server bundle correctly uses LimitChunkCountPlugin to prevent splitting.
  • CSS: Proper handling with exportOnlyLocals for SSR prevents unnecessary CSS in server bundle.

🔒 Security Assessment

Overall security posture is good for a demo application:

Good:

  • No obvious XSS vulnerabilities (React handles escaping)
  • Server bundles correctly isolated to private directory
  • No sensitive data in code
  • CSRF protection enabled (protect_from_forgery with: :exception)

⚠️ Watch:

  • request.fullpath passed to React (noted above - acceptable for demo)
  • No rate limiting or input validation (expected for demo)

📊 Test Coverage Assessment

Excellent coverage across:

  • ✅ SSR verification for all route types
  • ✅ Client-side navigation
  • ✅ Hydration consistency
  • ✅ Dynamic routes and params
  • ✅ Nested routes
  • ✅ Search parameters

Potential additions (optional):

  • Error boundary testing
  • 404 handling
  • Invalid route parameters
  • Back/forward navigation

🎓 Best Practices Adherence

This PR follows the repository's guidelines from CLAUDE.md:

  • ✅ Does NOT regenerate config files during setup
  • ✅ Preserves custom webpack configurations
  • ✅ Uses proper version constraints in Gemfile/package.json
  • ✅ Documents version manager usage for Conductor

📝 Recommendation

APPROVE with minor suggestions

This is production-quality demo code. The suggestions above are mostly educational improvements or clarifications rather than blocking issues. The implementation is solid, well-tested, and well-documented.

Before Merge:

  1. ✅ Ensure all CI checks pass
  2. ✅ Verify E2E tests pass in CI environment
  3. Consider addressing the bin/mise-exec portability point if you want broader shell compatibility

Great work! This will be a valuable resource for developers integrating TanStack Router with React on Rails.


🤖 Review by Claude Code

@claude
Copy link

claude bot commented Jan 14, 2026

PR Review: Add TanStack Router demo with SSR and TypeScript

Summary

This PR adds a comprehensive new demo (basic-v16-rspack-tanstack) showcasing TanStack Router integration with React on Rails SSR. The implementation is excellent overall with strong architecture, clear documentation, and thorough testing. A few minor suggestions below.


✅ Strengths

1. Excellent Architecture & Code Quality

  • Clean separation of concerns: Server/client components properly separated (TanStackApp.server.tsx vs TanStackApp.client.tsx)
  • Well-structured routing: File-based routing with proper type safety and validation
  • SSR configuration: Server webpack config properly handles CSS modules with exportOnlyLocals and disables file emission for images
  • Smart defaults: Uses Shakapacker 9.0+ privateOutputPath with graceful fallback

2. Outstanding Documentation

  • Comprehensive README with clear setup instructions
  • Inline code comments explain SSR limitations and trade-offs (e.g., async loader constraints)
  • CLAUDE.md updates provide clear guidance on mise/Conductor usage
  • Demo metadata file aids discoverability

3. Robust Testing

  • Comprehensive E2E tests covering:
    • SSR verification for all route types
    • Client-side hydration
    • Navigation without page reloads
    • Hydration mismatch detection
  • Tests properly wait for DOM states instead of relying on fragile networkidle

4. Security Best Practices

  • enforce_private_server_bundles = true prevents exposure of server-side code
  • CSRF protection enabled via protect_from_forgery
  • SSR bundles correctly placed outside public/ directory
  • Proper gitignore for generated files

💡 Suggestions for Improvement

1. Search Parameter Validation (Minor)

File: demos/basic-v16-rspack-tanstack/app/javascript/src/routes/search.tsx:10-19

The page number validation could be more robust:

page:
  typeof search.page === 'number'
    ? search.page
    : typeof search.page === 'string'
      ? parseInt(search.page, 10) || 1
      : 1,

Potential issue: Negative or zero page numbers aren't rejected. Consider:

page: (() => {
  const parsed = typeof search.page === 'number' 
    ? search.page 
    : typeof search.page === 'string' 
      ? parseInt(search.page, 10) 
      : NaN;
  return !isNaN(parsed) && parsed > 0 ? parsed : 1;
})(),

2. Shell Script Portability (Minor)

File: bin/mise-exec:1

The shebang #!/usr/bin/env zsh limits portability. Most users have bash, not all have zsh.

Suggestion: Either use #!/usr/bin/env bash or add a note in CLAUDE.md that zsh is required for this script.

3. Error Handling for Router Load (Low Priority)

File: demos/basic-v16-rspack-tanstack/app/javascript/src/TanStackApp/ror_components/TanStackApp.server.tsx:31

The void router.load() silently discards the promise. While documented as intentional, consider:

// More explicit about the sync-only constraint
const loadResult = router.load();
if (loadResult instanceof Promise) {
  // This is expected - we don't await, but loaders must be sync
  void loadResult;
}

This makes it clearer to future maintainers that the promise is intentionally ignored.

4. Test Timeout Hardcoded (Minor)

File: demos/basic-v16-rspack-tanstack/e2e/tanstack_router.spec.ts:121

await page.waitForTimeout(1000);

Hardcoded timeouts can be flaky in CI. Consider:

  • Using Playwright's auto-waiting mechanisms
  • Making timeout configurable via environment variable
  • Adding a comment explaining why 1000ms is needed

5. RuboCop Exclusion Pattern (Question)

File: .rubocop.yml:12

- 'demos/**/*'  # Each demo has its own Gemfile and rubocop config

This excludes ALL demos from root-level linting. Is this intentional? If demos should have their own RuboCop configs, this is fine. Just confirming it aligns with the project's linting strategy.


🔍 Performance Considerations

Positive

  • Rspack with SWC provides excellent build performance
  • CSS modules with exportOnlyLocals avoids duplicate CSS in SSR
  • Single-chunk server bundle (via LimitChunkCountPlugin) is optimal for SSR
  • devtool: 'eval' is perfect for fast SSR debugging

No concerns identified

Build configuration is well-optimized for both development and production.


🛡️ Security Review

✅ Good Practices

  1. Server bundle security: Properly enforced via enforce_private_server_bundles
  2. CSRF protection: Enabled in ApplicationController
  3. No secrets committed: .gitignore properly excludes credentials
  4. Input validation: Search params are validated and sanitized

⚠️ Minor Note

The demo doesn't sanitize user input for XSS in route params (e.g., /users/${userId}), but since this is a demo app without user-generated content, this is acceptable. For production apps, ensure params are properly escaped.


📊 Test Coverage

Excellent Coverage

  • ✅ SSR rendering for all route types
  • ✅ URL params and search params
  • ✅ Nested routes
  • ✅ Client-side navigation
  • ✅ Hydration consistency

Could Add (Optional)

  • 404/error page handling
  • Navigation with invalid route params
  • Browser back/forward navigation

But current coverage is more than sufficient for a demo.


🎯 Adherence to CLAUDE.md Guidelines

The PR perfectly follows the upgrade guidelines:

  • ✅ Only updates version numbers in Gemfile and package.json
  • ✅ Doesn't run rails shakapacker:install or regenerate configs
  • ✅ Preserves custom webpack configurations
  • ✅ Adds helpful bin/mise-exec wrapper per documentation

🚀 Recommendation

APPROVE with minor suggestions

This is a high-quality demo that:

  • Showcases advanced React on Rails features effectively
  • Provides excellent documentation
  • Follows best practices throughout
  • Has comprehensive test coverage

The suggestions above are minor refinements. The PR is ready to merge as-is if you prefer.

Great work! 🎉


📋 Test Plan Verification

From the PR description, I recommend checking off these items before merge:

  • Build passes: bin/shakapacker compiles without errors
  • RuboCop passes: bundle exec rubocop
  • E2E tests pass: npx playwright test
  • Manual verification of all route types

Reviewed by: Claude Code
Review date: 2026-01-14

@claude
Copy link

claude bot commented Jan 14, 2026

Pull Request Review: TanStack Router Demo

Overall Assessment

This is an excellent addition to the React on Rails demos repository. The implementation is well-structured, thoroughly documented, and demonstrates best practices for integrating TanStack Router with SSR. The code quality is high with comprehensive test coverage.


✅ Strengths

1. Excellent Documentation

  • Clear README with setup instructions, deployment guidance, and SSR limitations
  • Well-commented code explaining SSR synchronous rendering constraints
  • Helpful inline comments in configuration files (e.g., serverWebpackConfig.js)
  • CLAUDE.md additions provide valuable guidance for mise/asdf users

2. Strong Configuration Management

  • Proper use of Shakapacker 9.0+ private_output_path for SSR bundles (config/initializers/react_on_rails.rb:15-27)
  • Environment-aware webpack configuration with rspack/webpack compatibility
  • Good separation of client and server bundle configs
  • Security-conscious: enforce_private_server_bundles = true (config/initializers/react_on_rails.rb:32)

3. Comprehensive Testing

  • Excellent E2E test coverage with Playwright (e2e/tanstack_router.spec.ts)
  • Tests verify SSR rendering, hydration, navigation, and URL/search params
  • Hydration mismatch detection test is particularly valuable
  • Good use of domcontentloaded instead of networkidle for HMR environments

4. Type Safety

  • Full TypeScript implementation throughout
  • Type-safe route params and search params
  • Proper TypeScript module declarations for router registration

5. SSR Implementation

  • Clear documentation of synchronous SSR limitations (TanStackApp.server.tsx:12-22)
  • Proper memory history setup for server-side routing
  • Correct handling of initialUrl prop from Rails controller
  • All routes properly mapped in Rails routes file for SSR support

🔍 Observations & Suggestions

Minor Issues

1. Duplicate CSS Loader Logic (serverWebpackConfig.js:32-38 and 84-120)

The CSS loader filtering logic appears twice in serverWebpackConfig.js. The second, more comprehensive implementation (lines 84-120) handles CSS modules correctly with exportOnlyLocals: true, which is crucial for SSR. Consider removing or commenting out the first, simpler loop (lines 32-38) to avoid confusion.

2. Search Param Type Coercion (routes/search.tsx:14-17)

The search param validation has a minor inconsistency - it checks for both number and string types for page:

page:
  typeof search.page === 'number'
    ? search.page
    : typeof search.page === 'string'
      ? parseInt(search.page, 10) || 1
      : 1,

URL search params are always strings, so the first check (typeof search.page === 'number') will likely never be true. This is defensive but could be simplified. Not a bug, just redundant.

3. Inline Styles Throughout Components

The demo uses inline styles extensively (e.g., __root.tsx, search.tsx, users/.tsx). While this is fine for a demo, consider adding a note in the README suggesting CSS modules or styled-components for production applications (though the demo does demonstrate CSS modules with HelloWorld component).

4. Missing .tool-versions File

The new bin/mise-exec script is added, and CLAUDE.md extensively documents mise/asdf usage, but I don't see a .tool-versions file in the demo directory. Consider adding one for consistency.

Positive Observations

5. Excellent Error Handling

  • TanStack Router plugin wrapped in try-catch with warnings (clientWebpackConfig.js:28-42)
  • Precompile hook has proper error handling with stack traces (bin/shakapacker-precompile-hook:23-30)

6. Good .gitignore Coverage

Properly ignores generated files:

  • ssr-generated/
  • app/javascript/src/routeTree.gen.ts
  • app/javascript/generated/

7. Rails 8 Best Practices

  • Uses Propshaft instead of Sprockets
  • Modern allow_browser versions: :modern check
  • Health check endpoint configured

🔒 Security Review

✅ No Security Concerns Found

  1. CSRF Protection: Enabled via protect_from_forgery with: :exception (application_controller.rb:4)
  2. Server Bundle Security: enforce_private_server_bundles = true prevents exposure
  3. Input Validation: Search params are properly validated and sanitized
  4. No Vulnerable Dependencies: Gemfile.lock and package-lock.json use current versions
  5. No Secrets in Code: No hardcoded credentials or API keys

📊 Performance Considerations

✅ Good Performance Practices

  1. Rspack Usage: Significantly faster than webpack for builds
  2. HMR Support: Properly configured for fast development iteration
  3. Code Splitting: TanStack Router plugin has autoCodeSplitting: true
  4. Minimal Server Bundle: Single chunk with LimitChunkCountPlugin
  5. CSS Optimization: Server bundle excludes CSS extraction (client handles it)

💡 Potential Optimization

The router.load() call in TanStackApp.server.tsx:31 uses void to ignore the promise. While this is documented as intentional for synchronous SSR, it means routes without loaders work great, but any future async data fetching will require architectural changes. The documentation clearly states this limitation, which is good.


🧪 Test Coverage Assessment

✅ Excellent Coverage

The E2E test suite covers:

  • SSR rendering verification (all routes)
  • URL parameters (/users/:userId)
  • Search parameters (/search?q=hello&page=2)
  • Nested routes with outlets
  • Client-side navigation after hydration
  • Hydration mismatch detection

Suggestion: Consider adding a test for the HelloWorld component to verify the React on Rails auto-bundling feature works alongside TanStack Router.


📝 Documentation Quality

✅ Exceptional Documentation

  • README is comprehensive and well-organized
  • CLAUDE.md additions are highly valuable for contributors
  • Code comments explain complex decisions (e.g., SSR limitations)
  • Procfile variants documented with clear purposes
  • bin/mise-exec has excellent inline documentation

🎯 Recommendations

Required Before Merge

None - this PR is production-ready as-is.

Nice to Have (Optional)

  1. ✨ Remove duplicate CSS loader logic in serverWebpackConfig.js (lines 32-38 vs 84-120)
  2. 📄 Add .tool-versions file to demo directory for mise/asdf users
  3. 📝 Add note in README about CSS modules vs inline styles for production
  4. 🧪 Consider adding test for HelloWorld component auto-bundling

✅ Final Verdict

APPROVED - This is a high-quality demo that:

  • Follows React on Rails best practices
  • Has comprehensive documentation
  • Includes thorough test coverage
  • Demonstrates SSR correctly with clear limitation documentation
  • Maintains security best practices
  • Uses modern Rails 8 conventions

The minor suggestions above are truly optional improvements. The code is ready to merge as-is.

Great work! This will be a valuable resource for developers integrating TanStack Router with React on Rails. 🎉


Reviewed by: Claude Code Assistant
Review Date: 2026-01-14

@claude
Copy link

claude bot commented Jan 14, 2026

Code Review: TanStack Router Demo with SSR and TypeScript

I have completed a thorough review of this PR. Overall, this is excellent work with high-quality implementation. The demo effectively showcases TanStack Router integration with React on Rails SSR.

✅ Strengths

1. Excellent Documentation

  • Comprehensive README with clear setup instructions
  • Well-documented SSR limitations and async loader constraints
  • Inline code comments explaining critical concepts (e.g., void router.load() reasoning)
  • Detailed React on Rails Pro setup guide

2. Strong Architecture

  • Clean separation between client and server components
  • Proper SSR/hydration handling with initialUrl consistency
  • Intelligent router factory pattern with conditional history creation
  • Excellent error boundaries and 404 handling in __root.tsx

3. Comprehensive Testing

  • Excellent E2E test coverage with Playwright
  • Tests verify both SSR rendering AND client-side hydration
  • Smart test improvements: domcontentloaded instead of flaky networkidle
  • Hydration mismatch detection included

4. Configuration Quality

  • Webpack/Rspack dual support with proper plugin configuration
  • Correct target: node for server bundle
  • Proper CSS handling for SSR (exportOnlyLocals)
  • Good use of Shakapacker 9.0+ privateOutputPath

5. Security Best Practices

  • Proper .gitignore for credentials and generated files
  • Auth tokens excluded from version control
  • No sensitive data exposed in PR

6. Accessibility

  • Good use of aria-current for navigation links
  • Semantic HTML structure
  • Proper aria-label on navigation

💡 Suggestions & Minor Issues

1. Route Synchronization Pattern

Consider adding a comment in routes.rb explaining why each TanStack route needs a matching Rails route, and pointing to the catch-all pattern for production apps with many routes. This makes it clearer to future developers.

2. Error Handling in Webpack Config

In clientWebpackConfig.js, the try-catch blocks log warnings but continue. Consider if the build should fail if plugins are missing - currently, a missing plugin would result in a cryptic runtime error rather than a clear build-time error.

3. Client Component Null Safety

In TanStackApp.client.tsx, consider if initialUrl could be undefined - should there be a fallback to window.location.pathname to ensure hydration consistency?

🎯 Performance Considerations

  1. Bundle Size: TanStack Router is lightweight (~40KB gzipped)
  2. SSR Performance: Synchronous SSR is fast; Node renderer overhead is minimal
  3. HMR: Rspack with SWC will provide excellent dev experience (20x faster than Babel)

🛡️ Security Review

✅ No security concerns identified

  • Credentials properly excluded
  • No XSS vulnerabilities in route handling
  • Safe URL parameter handling
  • Pre-commit hooks prevent accidental token commits

📊 Test Coverage

✅ Excellent E2E coverage
Tests cover SSR rendering, URL params, search params, nested routes, client-side navigation, and hydration mismatch detection.

Suggestion: Consider adding one test for error boundary behavior.

🎓 Adherence to CLAUDE.md Guidelines

✅ Fully compliant

  • No generator commands used (avoiding config overwrites)
  • Custom webpack configurations preserved
  • Proper version pinning in Gemfile/package.json

🚀 Final Recommendation

APPROVE WITH MINOR SUGGESTIONS

This PR is ready to merge. The suggestions above are minor improvements that could be addressed in follow-up PRs. The core implementation is solid, well-tested, and well-documented.

Priority of Suggestions:

  • High: Error handling in webpack configs
  • Medium: Route synchronization documentation
  • Low: All other suggestions

Excellent work on this demo! 🎉

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@demos/basic-v16-rspack-tanstack/.env.example`:
- Line 8: Replace the weak example password for RENDERER_PASSWORD with a
non-secret placeholder or add a clear warning so devs don’t reuse it; update the
.env.example line for RENDERER_PASSWORD to either use CHANGE_ME_IN_PRODUCTION
(or similar) or append a comment like “DO NOT USE IN PRODUCTION — set a strong
unique password” so the environment variable RENDERER_PASSWORD is never shown as
a predictable default.
🧹 Nitpick comments (6)
demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js (1)

25-42: Warning-only error handling may mask configuration issues.

The try-catch blocks silently continue if the TanStack Router plugin fails to load, only logging a warning. While this allows the build to proceed, it could mask issues like missing dependencies or incorrect plugin versions in scenarios where the router is expected to function.

For a demo project this is reasonable, but for production usage consider either:

  • Throwing the error to fail fast when the plugin is required, or
  • Adding a configuration flag to control whether plugin loading is optional.
demos/basic-v16-rspack-tanstack/e2e/tanstack_router.spec.ts (2)

123-151: Console listener should be attached before navigation to catch all hydration warnings.

The page.on('console') listener is set up after page is obtained but the timing relative to page.goto() matters. While this typically works, attaching the listener before goto() ensures no early console messages are missed during the initial page load.

Additionally, the hydration error filter could be more comprehensive—React 19 uses terms like "Hydration failed" and "There was an error while hydrating".

♻️ Suggested improvement
     test('no hydration mismatch warnings', async ({ page }) => {
       const consoleLogs: string[] = [];
+      
+      // Attach listener before navigation to catch all messages
       page.on('console', (msg) => {
         if (msg.type() === 'error' || msg.type() === 'warning') {
           consoleLogs.push(msg.text());
         }
       });

       await page.goto('/');
       // ... rest of test ...

       // Check for hydration errors that may have been logged
       const hydrationErrors = consoleLogs.filter(
-        (log) => log.includes('hydrat') || log.includes('mismatch')
+        (log) => 
+          log.toLowerCase().includes('hydrat') || 
+          log.toLowerCase().includes('mismatch') ||
+          log.includes('did not match')
       );
       expect(hydrationErrors).toHaveLength(0);
     });

76-90: Search params test could benefit from URL encoding verification.

The test fills "test query" (with a space) and checks for /q=test/ in the URL. The space in "test query" will be URL-encoded, so the full match would be q=test%20query or q=test+query. The current regex /q=test/ passes but only partially validates the behavior.

♻️ More precise URL assertion
       // URL should update
-      await expect(page).toHaveURL(/q=test/);
+      await expect(page).toHaveURL(/q=test(%20|\+|%2B)?query/);

Or use a simpler single-word query to avoid encoding concerns:

       // Type in search input and submit
-      await input.fill('test query');
+      await input.fill('testquery');
       await page.click('button[type="submit"]');

       // URL should update
-      await expect(page).toHaveURL(/q=test/);
+      await expect(page).toHaveURL(/q=testquery/);
demos/basic-v16-rspack-tanstack/docs/react-on-rails-pro-setup.md (2)

90-94: Add language specifier to fenced code block.

The .npmrc example code block is missing a language identifier. Adding one improves syntax highlighting and accessibility.

📝 Suggested fix
-```
+```ini
 always-auth=true
 //npm.pkg.github.com/:_authToken=ghp_your_token_here
 `@shakacode-tools`:registry=https://npm.pkg.github.com
</details>

---

`160-165`: **Consider a health check instead of fixed sleep for CI reliability.**

The fixed `sleep 5` may be insufficient if the renderer takes longer to start, or wasteful if it starts faster. For improved reliability, consider polling a health endpoint.



<details>
<summary>📝 Example health check approach</summary>

```yaml
      - name: Start Node Renderer
        run: |
          cd react-on-rails-pro
          npm run node-renderer &
          # Wait for renderer to be ready (up to 30 seconds)
          for i in {1..30}; do
            curl -s http://localhost:3800/health && break || sleep 1
          done

Note: This assumes a /health endpoint exists. If not, the current approach is acceptable for a demo.

demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js (1)

29-38: Redundant filtering logic—consider consolidating.

This loop filters out mini-css-extract-plugin only when item is a string, but lines 88-98 perform a more comprehensive filter that handles both string and object loader items. This duplication may cause confusion during maintenance.

Consider removing this block and relying solely on the more complete filtering at lines 88-98, or consolidating both into a single pass.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 316c281 and f22d23d.

📒 Files selected for processing (19)
  • demos/basic-v16-rspack-tanstack/.env.example
  • demos/basic-v16-rspack-tanstack/.gitignore
  • demos/basic-v16-rspack-tanstack/Gemfile
  • demos/basic-v16-rspack-tanstack/Procfile.dev
  • demos/basic-v16-rspack-tanstack/README.md
  • demos/basic-v16-rspack-tanstack/app/javascript/packs/server-bundle.js
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/__root.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/about.tsx
  • demos/basic-v16-rspack-tanstack/bin/dev
  • demos/basic-v16-rspack-tanstack/config/initializers/react_on_rails.rb
  • demos/basic-v16-rspack-tanstack/config/initializers/react_on_rails_pro.rb
  • demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js
  • demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js
  • demos/basic-v16-rspack-tanstack/docs/react-on-rails-pro-setup.md
  • demos/basic-v16-rspack-tanstack/e2e/tanstack_router.spec.ts
  • demos/basic-v16-rspack-tanstack/playwright.config.ts
  • demos/basic-v16-rspack-tanstack/react-on-rails-pro/.npmrc.example
  • demos/basic-v16-rspack-tanstack/react-on-rails-pro/node-renderer.js
  • demos/basic-v16-rspack-tanstack/react-on-rails-pro/package.json
✅ Files skipped from review due to trivial changes (1)
  • demos/basic-v16-rspack-tanstack/react-on-rails-pro/.npmrc.example
🚧 Files skipped from review as they are similar to previous changes (6)
  • demos/basic-v16-rspack-tanstack/playwright.config.ts
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/about.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/packs/server-bundle.js
  • demos/basic-v16-rspack-tanstack/config/initializers/react_on_rails.rb
  • demos/basic-v16-rspack-tanstack/.gitignore
  • demos/basic-v16-rspack-tanstack/Procfile.dev
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

📄 CodeRabbit inference engine (CLAUDE.md)

When upgrading Shakapacker or React on Rails, identify breaking changes by reading the CHANGELOG for the version you're upgrading to. Apply ONLY the specific code changes mentioned in the changelog (e.g., API changes like Shakapacker.config to Shakapacker.configuration). Never use install generators to 'fix' breaking changes.

Files:

  • demos/basic-v16-rspack-tanstack/react-on-rails-pro/node-renderer.js
  • demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js
  • demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js
🧠 Learnings (9)
📓 Common learnings
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-14T06:21:07.270Z
Learning: During Shakapacker or React on Rails upgrades, follow a structured workflow: (1) Update version numbers only in Gemfile and package.json, (2) Run bundle install and npm install to update lock files, (3) Read the CHANGELOG for breaking changes, (4) Apply only specific breaking changes mentioned in changelog, (5) Test the application after each change. Never run install generators that regenerate config files.
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-14T06:21:07.270Z
Learning: Applies to **/*.js : When upgrading Shakapacker or React on Rails, identify breaking changes by reading the CHANGELOG for the version you're upgrading to. Apply ONLY the specific code changes mentioned in the changelog (e.g., API changes like `Shakapacker.config` to `Shakapacker.configuration`). Never use install generators to 'fix' breaking changes.
📚 Learning: 2026-01-14T06:21:07.270Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-14T06:21:07.270Z
Learning: Applies to **/*.js : When upgrading Shakapacker or React on Rails, identify breaking changes by reading the CHANGELOG for the version you're upgrading to. Apply ONLY the specific code changes mentioned in the changelog (e.g., API changes like `Shakapacker.config` to `Shakapacker.configuration`). Never use install generators to 'fix' breaking changes.

Applied to files:

  • demos/basic-v16-rspack-tanstack/README.md
  • demos/basic-v16-rspack-tanstack/.env.example
  • demos/basic-v16-rspack-tanstack/docs/react-on-rails-pro-setup.md
  • demos/basic-v16-rspack-tanstack/react-on-rails-pro/package.json
  • demos/basic-v16-rspack-tanstack/bin/dev
  • demos/basic-v16-rspack-tanstack/Gemfile
📚 Learning: 2026-01-14T06:21:07.270Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-14T06:21:07.270Z
Learning: During Shakapacker or React on Rails upgrades, follow a structured workflow: (1) Update version numbers only in Gemfile and package.json, (2) Run bundle install and npm install to update lock files, (3) Read the CHANGELOG for breaking changes, (4) Apply only specific breaking changes mentioned in changelog, (5) Test the application after each change. Never run install generators that regenerate config files.

Applied to files:

  • demos/basic-v16-rspack-tanstack/README.md
  • demos/basic-v16-rspack-tanstack/.env.example
  • demos/basic-v16-rspack-tanstack/docs/react-on-rails-pro-setup.md
  • demos/basic-v16-rspack-tanstack/react-on-rails-pro/package.json
  • demos/basic-v16-rspack-tanstack/bin/dev
  • demos/basic-v16-rspack-tanstack/Gemfile
📚 Learning: 2026-01-14T06:21:07.270Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-14T06:21:07.270Z
Learning: Applies to config/initializers/react_on_rails.rb : NEVER overwrite config/initializers/react_on_rails.rb during upgrades. This file contains project-specific React on Rails initialization settings and must be preserved. When upgrading React on Rails, only update version numbers in Gemfile and package.json - never run `rails react_on_rails:install`.

Applied to files:

  • demos/basic-v16-rspack-tanstack/README.md
  • demos/basic-v16-rspack-tanstack/.env.example
  • demos/basic-v16-rspack-tanstack/react-on-rails-pro/node-renderer.js
  • demos/basic-v16-rspack-tanstack/docs/react-on-rails-pro-setup.md
  • demos/basic-v16-rspack-tanstack/react-on-rails-pro/package.json
  • demos/basic-v16-rspack-tanstack/bin/dev
  • demos/basic-v16-rspack-tanstack/config/initializers/react_on_rails_pro.rb
  • demos/basic-v16-rspack-tanstack/Gemfile
📚 Learning: 2026-01-14T06:21:07.270Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-14T06:21:07.270Z
Learning: Applies to Gemfile : When upgrading Shakapacker or React on Rails, ONLY update version numbers in Gemfile - gem version constraints. Do not run install commands like `rails shakapacker:install` or `rails react_on_rails:install` that regenerate config files.

Applied to files:

  • demos/basic-v16-rspack-tanstack/README.md
  • demos/basic-v16-rspack-tanstack/.env.example
  • demos/basic-v16-rspack-tanstack/docs/react-on-rails-pro-setup.md
  • demos/basic-v16-rspack-tanstack/bin/dev
  • demos/basic-v16-rspack-tanstack/Gemfile
📚 Learning: 2026-01-14T06:21:07.270Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-14T06:21:07.270Z
Learning: Applies to package.json : When upgrading Shakapacker or React on Rails, ONLY update version numbers in package.json - npm package versions. Do not run install commands that regenerate config files.

Applied to files:

  • demos/basic-v16-rspack-tanstack/README.md
  • demos/basic-v16-rspack-tanstack/.env.example
  • demos/basic-v16-rspack-tanstack/docs/react-on-rails-pro-setup.md
  • demos/basic-v16-rspack-tanstack/react-on-rails-pro/package.json
  • demos/basic-v16-rspack-tanstack/bin/dev
  • demos/basic-v16-rspack-tanstack/Gemfile
📚 Learning: 2026-01-14T06:21:07.270Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-14T06:21:07.270Z
Learning: Applies to config/webpack/*.js : Preserve all custom configurations in config/webpack/ directory files. These files contain project-specific webpack settings and should never be overwritten during Shakapacker upgrades.

Applied to files:

  • demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js
  • demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js
📚 Learning: 2026-01-14T06:21:07.270Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-14T06:21:07.270Z
Learning: Applies to config/webpack/webpack.config.js : NEVER replace or overwrite config/webpack/webpack.config.js during upgrades. This file contains custom logic specific to the project and must be preserved. When upgrading Shakapacker, only update version numbers and run bundle install/npm install - never run `rails shakapacker:install`.

Applied to files:

  • demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js
  • demos/basic-v16-rspack-tanstack/react-on-rails-pro/package.json
  • demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js
  • demos/basic-v16-rspack-tanstack/Gemfile
📚 Learning: 2026-01-14T06:21:07.270Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-14T06:21:07.270Z
Learning: Applies to config/shakapacker.yml : NEVER overwrite or revert config/shakapacker.yml during upgrades. This file contains project-specific settings and must be preserved. When upgrading Shakapacker, only update version numbers in Gemfile and package.json, then run bundle install and npm install - never run `rails shakapacker:install`.

Applied to files:

  • demos/basic-v16-rspack-tanstack/Gemfile
🧬 Code graph analysis (4)
demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js (2)
demos/basic-v16-rspack-tanstack/config/webpack/commonWebpackConfig.js (2)
  • require (5-5)
  • commonWebpackConfig (16-16)
demos/basic-v16-rspack-tanstack/config/webpack/generateWebpackConfigs.js (1)
  • serverWebpackConfig (5-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/__root.tsx (8)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/about.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/deep.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/search.tsx (1)
  • Route (9-22)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/$userId.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js (6)
demos/basic-v16-rspack-tanstack/config/webpack/webpack.config.js (4)
  • path (6-6)
  • require (1-1)
  • require (2-2)
  • require (3-3)
demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js (2)
  • require (4-4)
  • commonWebpackConfig (5-5)
demos/basic-v16-rspack-tanstack/config/webpack/commonWebpackConfig.js (2)
  • require (5-5)
  • commonWebpackConfig (16-16)
demos/basic-v16-rspack-tanstack/config/webpack/development.js (1)
  • require (4-4)
demos/basic-v16-rspack-tanstack/config/webpack/generateWebpackConfigs.js (1)
  • clientConfig (8-8)
demos/basic-v16-rspack-tanstack/config/application.rb (1)
  • config (22-41)
demos/basic-v16-rspack-tanstack/config/initializers/react_on_rails_pro.rb (1)
demos/basic-v16-rspack-tanstack/react-on-rails-pro/node-renderer.js (1)
  • config (21-39)
🪛 markdownlint-cli2 (0.18.1)
demos/basic-v16-rspack-tanstack/docs/react-on-rails-pro-setup.md

90-90: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: claude-review
🔇 Additional comments (29)
demos/basic-v16-rspack-tanstack/bin/dev (1)

1-26: LGTM!

Clean, well-documented development server script. The delegation to ReactOnRails::Dev::ServerManager keeps this file minimal while the documentation clearly explains the available Procfile modes (hmr, static, prod) and customization options. The previous issue regarding the stale DEFAULT_ROUTE constant has been properly addressed.

demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js (4)

1-6: LGTM!

Imports are consistent with other webpack configuration files in this project, properly using shakapacker's config export for bundler detection.


8-15: LGTM!

Good defensive coding to remove the server-bundle entry from the client configuration. The comment clearly explains the rationale for this separation.


17-23: LGTM!

Plugin configuration is well-structured with appropriate settings for TanStack Router. Path resolution using path.resolve(__dirname, ...) ensures correct absolute paths regardless of the working directory.


44-47: LGTM!

Export pattern correctly exposes the configureClient function, matching the expected usage in generateWebpackConfigs.js where it's called as clientWebpackConfig().

demos/basic-v16-rspack-tanstack/README.md (1)

1-231: Well-structured and comprehensive documentation.

The README provides excellent coverage of setup, configuration, SSR requirements, and limitations. The warning about React on Rails Pro requirement is appropriately prominent, and the explanation of why the Node renderer is needed (TanStack Router's use of setTimeout/clearTimeout) is helpful context.

demos/basic-v16-rspack-tanstack/app/javascript/src/routes/__root.tsx (3)

1-8: Root route setup follows TanStack Router conventions correctly.

The route configuration properly defines component, errorComponent, and notFoundComponent. JavaScript function hoisting allows the component references to work despite being defined later in the file.


62-68: Good pattern for shared link styles with proper TypeScript typing.

The as const assertion on fontWeight correctly narrows the type to the literal 'bold' rather than string, avoiding TypeScript errors when passing to style props.


74-125: Well-implemented navigation with proper accessibility.

Good use of aria-label on the nav element and aria-current: 'page' via activeProps for screen reader support. The activeOptions: { exact: true } on the Home link correctly prevents it from being marked active on all routes.

demos/basic-v16-rspack-tanstack/e2e/tanstack_router.spec.ts (2)

5-14: SSR verification tests look good.

The tests appropriately use waitForLoadState('domcontentloaded') to verify content is present without waiting for JavaScript execution, which correctly validates server-side rendering.


61-74: Client hydration navigation test is well-structured.

The test correctly verifies that client-side navigation works after hydration by clicking a link and asserting the URL change without a full page reload. Using domcontentloaded with element visibility checks is a reliable pattern for HMR environments.

demos/basic-v16-rspack-tanstack/react-on-rails-pro/package.json (1)

1-10: LGTM!

The package.json is well-structured with:

  • private: true to prevent accidental publishing
  • Pinned dependency version for reproducibility
  • Simple, clear script for starting the renderer
demos/basic-v16-rspack-tanstack/docs/react-on-rails-pro-setup.md (1)

1-47: Well-structured setup documentation.

The guide clearly explains why React on Rails Pro is required, provides straightforward license request instructions, and documents the token creation process. This will help developers get started quickly.

demos/basic-v16-rspack-tanstack/Gemfile (1)

27-37: Release candidate version in use - verify stability before wider adoption.

The demo uses react_on_rails version 16.2.0.rc1. This is appropriate for demonstrating new features, but consumers of this demo should be aware it's a pre-release version.

The GitHub Packages source block for react_on_rails_pro is correctly configured with clear documentation pointing to setup instructions.

demos/basic-v16-rspack-tanstack/config/initializers/react_on_rails_pro.rb (2)

14-27: Excellent developer experience with clear error messaging.

The guard clause with detailed setup instructions will help developers understand what's missing and how to resolve it. The multi-line error message is informative without being overwhelming.


29-48: Configuration aligns with Node Renderer - consider documenting the shared password requirement.

The configuration correctly matches the Node Renderer defaults:

  • renderer_url default matches port 3800
  • renderer_password default matches "tanstack-demo-renderer"

The renderer_use_fallback_execjs = false is the right choice since ExecJS cannot handle TanStack Router's timer APIs.

One observation: the default password "tanstack-demo-renderer" appears in both this file and node-renderer.js. While acceptable for a demo, production deployments should use unique credentials via environment variables. The inline comment could note this.

demos/basic-v16-rspack-tanstack/react-on-rails-pro/node-renderer.js (3)

1-14: Clear documentation of configuration options.

The JSDoc header effectively documents the renderer's purpose and available environment variables. This makes it easy for developers to customize the setup.


46-50: Good security practice: password excluded from console output.

The configuration logging correctly omits the password field, preventing accidental credential exposure in logs.


21-39: Configuration is well-structured with sensible defaults.

The config object uses environment variables with appropriate fallbacks. The defaults align with the Rails initializer (react_on_rails_pro.rb), ensuring consistency between the Ruby and Node components.

demos/basic-v16-rspack-tanstack/.env.example (1)

1-4: LGTM!

The React on Rails Pro token configuration is well-documented with clear instructions on how to obtain a license and create the required GitHub PAT.

demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js (9)

1-8: LGTM!

The dynamic bundler selection based on config.assets_bundler is a clean approach for supporting both Rspack and Webpack, and the imports follow Shakapacker conventions.


9-27: LGTM!

The defensive check for missing server-bundle entry with an actionable error message is well implemented. The comment explaining the mutable global issue is helpful for maintainability.


40-44: LGTM!

Disabling minimization and enforcing a single chunk with LimitChunkCountPlugin is appropriate for server bundles—simplifies debugging and aligns with SSR requirements.


46-72: LGTM!

The output configuration with privateOutputPath fallback and informative warning message provides good guidance for users migrating to Shakapacker 9.0+. The commonjs2 library target is correct for Node.js server rendering.


74-81: LGTM!

Removing WebpackAssetsManifest, MiniCssExtractPlugin, and ForkTsCheckerWebpackPlugin is appropriate for server bundles—CSS extraction and manifest generation are client-side concerns, and type checking once is sufficient.


88-120: Good defensive handling in loader callbacks.

The filter and find callbacks now initialize testValue = '' and include null checks (item &&), addressing the potential TypeError issues from previous reviews.


122-133: LGTM!

Setting devtool: 'eval' for fast SSR debugging and target: 'node' for Node.js API support is appropriate. The comment explaining the React on Rails Pro Node renderer requirement provides helpful context.


135-135: LGTM!

The export pattern is consistent with usage in generateWebpackConfigs.js.


109-111: > Likely an incorrect or invalid review comment.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

justin808 and others added 12 commits January 14, 2026 17:27
Base demo app created with:
- Rails 8.0.3 with PostgreSQL
- Shakapacker 9.3.4 with Rspack bundler
- React on Rails 16.2.0.rc1 with SSR
- TypeScript support
- SWC transpiler (20x faster than Babel)
- HelloWorld example component

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Install @tanstack/react-router for routing
- Install @tanstack/router-plugin for file-based routing
- Install @tanstack/react-router-devtools for debugging
- Configure TanStack Router plugin in clientWebpackConfig.js
- Add .gitignore with routeTree.gen.ts exclusion
- Create initial __root.tsx route file

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Routes:
- __root.tsx: Root layout with navigation
- index.tsx: Home page
- about.tsx: About page with SSR explanation
- search.tsx: Search params demo with pagination
- users/index.tsx: Users list
- users/$userId.tsx: URL params demo
- demo/nested.tsx: Nested layout with outlet
- demo/nested/index.tsx: Nested index page
- demo/nested/deep.tsx: Deep nested page

Components:
- router.ts: Router factory with SSR support
- TanStackApp.client.tsx: Client-side React on Rails component
- TanStackApp.server.tsx: Server-side React on Rails component

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Controller:
- Create TanstackAppController with initialUrl prop for SSR

Views:
- Create tanstack_app/index.html.erb with react_component helper
- Create layouts/tanstack_app.html.erb with pack tags

Routes:
- Add root route pointing to tanstack_app#index
- Add direct routes for SSR: /about, /search, /users, /demo/nested, etc.
- Remove old hello_world routes

Cleanup:
- Remove HelloWorld controller, views, and layout

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests:
- Server-Side Rendering Verification
  - Home page is server-rendered
  - About page is server-rendered at correct route
  - URL params are server-rendered correctly (/users/123)
  - Search params are server-rendered correctly (/search?q=hello&page=2)
  - Nested routes are server-rendered

- Client Hydration Verification
  - Navigation works after hydration
  - Search params update client-side
  - URL params navigation works
  - Nested routes navigation works

- SSR + Hydration Consistency
  - No hydration mismatch warnings

Configuration:
- Add playwright.config.ts with web server config
- Install @playwright/test dependency

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
High priority:
- Remove committed generated artifacts (ssr-generated/) from git tracking
- Update .gitignore to exclude additional generated directories
  (app/javascript/generated/, packs/generated/, test-results/)

Medium priority:
- Document SSR async loader limitations in TanStackApp.server.tsx
- Use `void router.load()` to explicitly indicate non-awaited promise
- Fix Playwright tests: replace flaky networkidle with domcontentloaded
- Add explicit element visibility waits before interactions
- Fix client component to pass initialUrl for hydration consistency

Low priority:
- Update bin/dev DEFAULT_ROUTE from hello_world to /
- Add npm scripts for E2E testing (test, test:ui, test:headed)
- Expand README with E2E testing and SSR limitations sections

Also includes:
- Null safety improvements in serverWebpackConfig.js
- Minor fixes from rebased changes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use bin/mise-exec to ensure correct Ruby version from .tool-versions
- Run rubocop from within demo directories for demo files since each
  demo has its own Gemfile and rubocop config (root excludes demos/)
- Add frozen_string_literal to ApplicationHelper for consistency

This fixes the pre-commit hook failing with "Your Ruby version is 2.6"
when running in non-interactive shells like Conductor.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
RuboCop was only checking files but not auto-fixing them, unlike
ESLint (--fix) and Prettier (--write) which auto-correct issues.

Added --autocorrect flag to rubocop commands so that auto-correctable
offenses are fixed automatically, consistent with the other linters.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The TanStack Router plugin was only configured for Rspack bundler.
If someone switched to Webpack using bin/switch-bundler, the route
tree wouldn't be auto-generated, breaking the file-based routing.

- Extract router plugin config to shared object
- Add else branch for Webpack bundler using TanStackRouterWebpack
- Both bundlers now have proper route tree generation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
TanStack Router requires Node.js APIs (setTimeout, clearTimeout, etc.)
that are not available in the default ExecJS environment. This commit
adds React on Rails Pro with the Node Renderer as a required dependency.

Key changes:
- Add react_on_rails_pro gem (required, not optional)
- Add react-on-rails-pro/ directory with Node Renderer configuration
- Add config/initializers/react_on_rails_pro.rb with Node Renderer setup
- Add comprehensive setup documentation in docs/react-on-rails-pro-setup.md
- Update Procfile.dev to include Node Renderer process
- Remove ExecJS polyfills - no fallback, Pro is required
- Add .env.example for environment variable configuration

Setup instructions:
1. Get a free license: Contact team@shakacode.com
2. Configure bundler for GitHub Packages
3. Run bundle install
4. Set up Node Renderer: cd react-on-rails-pro && npm install
5. Start with bin/dev

Related issues:
- Documentation: shakacode/react_on_rails#2299
- TanStack Router: shakacode/react_on_rails#2298

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
React on Rails Pro is now available on regular package registries:
- Ruby gem from rubygems.org (no longer GitHub Packages)
- npm package from npmjs.org (no longer @shakacode-tools scope)

Changes:
- Update Gemfile to use regular gem source
- Update react-on-rails-pro/package.json for regular npm
- Remove .npmrc.example (no auth needed)
- Simplify documentation to remove GitHub Packages auth setup
- Update README with clearer setup instructions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@justin808 justin808 force-pushed the jg/tanstack-router-demo branch from f22d23d to 72b4250 Compare January 15, 2026 04:12
@claude
Copy link

claude bot commented Jan 15, 2026

PR Review: TanStack Router Demo with SSR and TypeScript

I've reviewed this PR and it's an excellent addition to the demo suite! The implementation is well-thought-out and demonstrates best practices throughout. Here's my comprehensive feedback:


✅ Strengths

1. Architecture & Design

  • Excellent webpack configuration: The split between client and server configs is clean, with proper handling of SSR-specific concerns (CSS modules with exportOnlyLocals, no file emission, etc.)
  • Smart bundler abstraction: The config.assets_bundler checks in both client and server configs enable seamless switching between webpack and rspack
  • Proper SSR setup: Using target: 'node' and libraryTarget: 'commonjs2' for the server bundle is exactly right for the Node renderer
  • Clear separation of concerns: Server and client components are properly separated with appropriate comments explaining limitations

2. Code Quality

  • TypeScript usage: Proper typing throughout, with good use of interfaces and type safety
  • React best practices: Using React.useState for router initialization in client component prevents recreation on re-renders
  • Error handling: Root route includes proper error boundaries and 404 handling
  • Accessibility: Navigation uses proper aria-current attributes and semantic HTML

3. Documentation

  • Comprehensive README: Excellent documentation of setup, configuration, limitations, and deployment
  • Inline comments: Great explanatory comments in complex configs (e.g., serverWebpackConfig.js lines 10-13 explaining the mutation issue)
  • Clear warnings: The SSR limitations are well-documented in both code comments and README

4. Testing

  • Thorough E2E coverage: Tests cover SSR verification, client hydration, URL params, search params, nested routes, and hydration mismatches
  • Smart test strategies: Using domcontentloaded instead of networkidle is the right choice for HMR environments
  • Practical assertions: Tests verify actual functionality rather than implementation details

5. Security & Best Practices

  • Proper .gitignore: Correctly excludes generated files, credentials, and auth tokens
  • Environment variable handling: Good use of .env.example with safe defaults
  • No hardcoded secrets: Authentication uses environment variables with fallback defaults
  • Command injection prevention: The bin/switch-bundler script properly uses array form for system commands (lines 100-117)

🔍 Areas for Consideration

1. Node Renderer Dependency Reference

Location: react-on-rails-pro/node-renderer.js:19

The import uses:

const { reactOnRailsProNodeRenderer } = require('@shakacode-tools/react-on-rails-pro-node-renderer');

However, based on the commit message stating "React on Rails Pro is now available on regular package registries" and the package being named react-on-rails-pro in npm, you may want to verify this package name is correct. The scoped @shakacode-tools namespace might be outdated.

Impact: Low (would fail at startup if incorrect, easy to catch)

2. React on Rails Pro License Documentation

Location: Various files mention contacting for a "free license"

The documentation mentions getting a "free license" but then shows the gem is available on RubyGems. This might be confusing to users. Consider clarifying:

  • Is the license free for open-source/demo use?
  • What are the actual licensing terms?
  • Is authentication still required via REACT_ON_RAILS_PRO_TOKEN?

The .env.example references a GitHub PAT token, but if it's on regular RubyGems/npm, this shouldn't be needed anymore.

Recommendation: Clarify the licensing model in the README and potentially remove references to GitHub PAT if no longer needed.

3. Hydration Consistency Edge Case

Location: app/javascript/src/TanStackApp/ror_components/TanStackApp.client.tsx:10

The client component receives initialUrl?: string as optional, but the server component has it as required. While this works, there's a potential hydration mismatch if the server renders with a URL but the client doesn't receive it.

Current code:

const TanStackApp: React.FC<TanStackAppProps> = ({ initialUrl }) => {
  const [router] = React.useState(() => createRouter({ initialUrl }));

Suggestion: Either make it required in the client interface too, or add a runtime check/default:

const [router] = React.useState(() => 
  createRouter({ initialUrl: initialUrl || window.location.pathname })
);

Impact: Low (appears to work in practice, but could prevent subtle bugs)

4. Test Reliability Enhancement

Location: e2e/tanstack_router.spec.ts:123-151

The hydration mismatch test uses a click interaction to verify hydration completed, which is clever. However, you could make it more explicit:

// Alternative: Wait for React DevTools global or a data attribute
await page.waitForFunction(() => {
  return document.querySelector('[data-tanstack-router-id]') \!== null;
});

Impact: Very low (current approach is fine, this is just an alternative)

5. Bundle Size Consideration

The demo includes both webpack and rspack dependencies. For actual projects (not demos), users would only need one. Consider adding a note in the README about removing unused bundler dependencies in production.


🛡️ Security Review

No security concerns identified

  • No XSS vulnerabilities (proper React escaping)
  • No command injection (array form used in shell commands)
  • Credentials properly excluded from git
  • Server bundles in private directory with enforce_private_server_bundles: true
  • No eval or unsafe dynamic code execution
  • Dependencies are recent and well-maintained

📊 Test Coverage Assessment

Coverage: Excellent

The test suite covers:

  • ✅ SSR verification (home, about, users, search, nested routes)
  • ✅ URL parameter handling
  • ✅ Search parameter handling
  • ✅ Client-side navigation after hydration
  • ✅ Hydration consistency
  • ✅ Error logging verification

Missing tests (Nice to have, not critical for demo):

  • Error boundary behavior
  • 404 page rendering
  • Network failure scenarios

🎯 Performance Considerations

  1. Rspack configuration is optimal: Using SWC transpiler for maximum build speed
  2. Server bundle optimization: Single chunk, no splitting (correct for SSR)
  3. Client optimization: Default code splitting enabled via TanStack Router's autoCodeSplitting: true
  4. CSS handling: Proper extraction on client, exportOnlyLocals on server (no duplicate CSS)

Suggestion: Consider adding a performance budget or bundle size tracking in future iterations.


📝 Best Practices Adherence

Follows CLAUDE.md Guidelines ✅

  1. No config overwrites: The upgrade path doesn't run install generators
  2. Custom configurations preserved: All webpack configs are custom and well-documented
  3. Version pinning: Uses specific versions (16.2.0.rc1) not floating
  4. Proper bin/conductor-exec usage: Pre-commit hooks properly configured

React on Rails Best Practices ✅

  1. ✅ File-based component registry (components_subdirectory)
  2. ✅ Proper server bundle path configuration
  3. enforce_private_server_bundles: true for security
  4. ✅ Comprehensive comments explaining configuration choices

🚀 Recommendations Summary

High Priority

None - the PR is production-ready as-is for a demo

Medium Priority

  1. Clarify licensing documentation - Remove confusion about "free license" vs public npm/RubyGems availability
  2. Verify @shakacode-tools package name - Ensure it matches the actual published package

Low Priority (Polish)

  1. Make initialUrl required in client component interface for consistency
  2. Add bundle size notes to README for production usage
  3. Consider adding error boundary and 404 tests

✨ Conclusion

Recommendation: APPROVE ✅

This is an excellent demo that showcases TanStack Router integration with React on Rails in a professional, well-documented manner. The code quality is high, security practices are sound, and the documentation is thorough. The few suggestions above are minor improvements rather than critical issues.

Outstanding aspects:

  • Exemplary documentation (README, inline comments, separate docs/)
  • Comprehensive test coverage
  • Clean architecture with proper separation of concerns
  • Security best practices followed throughout
  • Excellent educational value for users learning React on Rails SSR

Great work! This will be a valuable addition to the demo suite.


Review conducted by Claude Code - PR #104 - shakacode/react_on_rails-demos

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@demos/basic-v16-rspack-tanstack/bin/switch-bundler`:
- Around line 84-86: When writing package_json_path using File.write and
JSON.pretty_generate, ensure the output ends with a trailing newline by
appending "\n" to the generated JSON string (i.e., write
JSON.pretty_generate(package_json) + "\n"); update the write call where
package_json_path is used so files produced by switch-bundler include the final
newline.

In `@demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js`:
- Around line 114-119: The code assumes rule.use.options exists before setting
emitFile; add a null check or initialize options when handling loaders
'url-loader' or 'file-loader' so you don't access undefined. Specifically,
inside the branch that checks rule.use and (rule.use.loader === 'url-loader' ||
rule.use.loader === 'file-loader'), ensure rule.use.options is defined (e.g., if
(!rule.use.options) rule.use.options = {}) before assigning
rule.use.options.emitFile = false to avoid a TypeError.

In `@demos/basic-v16-rspack-tanstack/README.md`:
- Line 16: The README contains an incorrect "Created" date string ("Created:
2025-01-13"); verify whether this is intentional for demo versioning or a
mistake and update the "Created: 2025-01-13" line in
demos/basic-v16-rspack-tanstack/README.md to the correct date (e.g., the PR
creation date in January 2026) or add a clarifying note about versioning if the
older date is intentional.
🧹 Nitpick comments (12)
demos/basic-v16-rspack-tanstack/bin/shakapacker-precompile-hook (1)

23-30: Consider adding a success message for consistency with the documented emoji scheme.

The emoji scheme in lines 11-14 documents ✅ for success, but no success message is printed when generate_packs_if_stale completes. Adding one would provide better feedback during builds and maintain consistency with your documented conventions.

♻️ Suggested improvement
 begin
   puts Rainbow("🔄 Running React on Rails precompile hook...").cyan
   ReactOnRails::PacksGenerator.instance.generate_packs_if_stale
+  puts Rainbow("✅ React on Rails precompile hook completed.").green
 rescue StandardError => e
   warn Rainbow("❌ Error in precompile hook: #{e.message}").red
   warn e.backtrace.first(5).join("\n")
   exit 1
 end
demos/basic-v16-rspack-tanstack/bin/switch-bundler (2)

4-4: Unused require.

fileutils is required but never used in this script.

🧹 Proposed cleanup
-require "fileutils"

124-129: Consider adding Bun support.

The current detection covers the major package managers. For future-proofing, you could optionally add Bun detection.

💡 Optional: Add Bun support
   def detect_package_manager
     return "yarn" if File.exist?("yarn.lock")
     return "pnpm" if File.exist?("pnpm-lock.yaml")
+    return "bun" if File.exist?("bun.lockb")

     "npm"
   end

You would also need to add the corresponding Bun install commands in install_dependencies:

when "bun"
  system("bun", "add", *deps[:dependencies])
# and
when "bun"
  system("bun", "add", "-d", *deps[:dev_dependencies])
demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js (1)

31-41: Consider surfacing plugin load failures more prominently in development.

Silent console.warn may be missed during development. If the TanStack Router plugin fails to load, file-based routing won't work and the app will break at runtime.

💡 Optional: throw in development for faster feedback
   if (config.assets_bundler === 'rspack') {
     try {
       const { TanStackRouterRspack } = require('@tanstack/router-plugin/rspack');
       clientConfig.plugins.push(TanStackRouterRspack(routerPluginConfig));
     } catch (e) {
-      console.warn('TanStack Router Rspack plugin not available:', e.message);
+      console.error('TanStack Router Rspack plugin not available:', e.message);
+      if (process.env.NODE_ENV === 'development') {
+        throw e;
+      }
     }
   } else {
     // Webpack bundler
     try {
       const { TanStackRouterWebpack } = require('@tanstack/router-plugin/webpack');
       clientConfig.plugins.push(TanStackRouterWebpack(routerPluginConfig));
     } catch (e) {
-      console.warn('TanStack Router Webpack plugin not available:', e.message);
+      console.error('TanStack Router Webpack plugin not available:', e.message);
+      if (process.env.NODE_ENV === 'development') {
+        throw e;
+      }
     }
   }
demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js (2)

32-38: Duplicate filtering logic—consider removing this block.

This filtering logic is superseded by the more comprehensive version at lines 88-98, which handles both string and object loader items. This first pass only handles string items and the filter is applied again later.

♻️ Remove redundant first-pass filter
-  // Remove the mini-css-extract-plugin from the style loaders because
-  // the client build will handle exporting CSS.
-  // replace file-loader with null-loader
-  serverWebpackConfig.module.rules.forEach((loader) => {
-    if (loader.use && loader.use.filter) {
-      loader.use = loader.use.filter(
-        (item) => !(typeof item === 'string' && item.match(/mini-css-extract-plugin/))
-      );
-    }
-  });
-
   // No splitting of chunks for a server bundle

65-72: Warning message is misleading—no version check performed.

The warning claims "Shakapacker 9.0+ detected" but there's no actual version detection. This warning fires whenever privateOutputPath is falsy, which could also occur due to configuration omission in any Shakapacker version.

📝 Suggested wording fix
   if (!config.privateOutputPath) {
     console.warn(
-      '⚠️  Shakapacker 9.0+ detected but private_output_path not configured in shakapacker.yml'
+      '⚠️  private_output_path not configured in shakapacker.yml (required for Shakapacker 9.0+)'
     );
demos/basic-v16-rspack-tanstack/config/shakapacker.yml (1)

1-105: Consider reducing configuration duplication with YAML anchors.

The configuration repeats many identical values across environments. While the current explicit form aids readability, you could reduce maintenance burden by anchoring the common default block and using environment-specific overrides.

This is optional since the current format is explicit and follows Shakapacker conventions. Based on learnings, this file contains project-specific settings that should be preserved during upgrades, so either approach works.

demos/basic-v16-rspack-tanstack/config/webpack/development.js (1)

4-4: Unused imports from shakapacker.

devServer and inliningCss are imported but not used in this file. Only config is actually referenced.

🔧 Suggested fix
-const { devServer, inliningCss, config } = require('shakapacker');
+const { config } = require('shakapacker');
demos/basic-v16-rspack-tanstack/Gemfile (1)

27-32: Consider adding a version constraint for react_on_rails_pro.

While react_on_rails has a specific version pinned (16.2.0.rc1), react_on_rails_pro has no version constraint. This could lead to unexpected behavior if a breaking change is released.

The comment explaining the Node.js API requirement is excellent documentation.

demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/index.tsx (1)

7-14: Consider adding a TypeScript interface for the user data.

For a TypeScript demo, adding explicit types would better showcase type-safety benefits:

🔧 Optional type definition
+interface User {
+  id: number;
+  name: string;
+  email: string;
+}
+
 // Mock user data
-const users = [
+const users: User[] = [
   { id: 1, name: 'Alice Johnson', email: 'alice@example.com' },
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/search.tsx (1)

4-7: Type inconsistency: page is always defined after validation.

The validateSearch function always returns a page value (defaulting to 1), but SearchParams declares page?: number as optional. This creates a mismatch between the type and runtime behavior.

Suggested fix
 type SearchParams = {
   q?: string;
-  page?: number;
+  page: number;
 };
demos/basic-v16-rspack-tanstack/playwright.config.ts (1)

17-22: Consider adding Firefox/WebKit projects for broader coverage.

Currently only Chromium is configured. For a demo showcasing SSR, testing across browsers could catch browser-specific hydration issues. This is optional for a demo app.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f22d23d and 72b4250.

⛔ Files ignored due to path filters (5)
  • demos/basic-v16-rspack-tanstack/Gemfile.lock is excluded by !**/*.lock
  • demos/basic-v16-rspack-tanstack/package-lock.json is excluded by !**/package-lock.json
  • demos/basic-v16-rspack-tanstack/public/icon.png is excluded by !**/*.png
  • demos/basic-v16-rspack-tanstack/public/icon.svg is excluded by !**/*.svg
  • demos/basic-v16-rspack-tanstack/react-on-rails-pro/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (100)
  • demos/basic-v16-rspack-tanstack/.demo-metadata.yml
  • demos/basic-v16-rspack-tanstack/.dev-services.yml.example
  • demos/basic-v16-rspack-tanstack/.env.example
  • demos/basic-v16-rspack-tanstack/.gitignore
  • demos/basic-v16-rspack-tanstack/.rubocop.yml
  • demos/basic-v16-rspack-tanstack/Gemfile
  • demos/basic-v16-rspack-tanstack/Procfile.dev
  • demos/basic-v16-rspack-tanstack/Procfile.dev-prod-assets
  • demos/basic-v16-rspack-tanstack/Procfile.dev-static-assets
  • demos/basic-v16-rspack-tanstack/README.md
  • demos/basic-v16-rspack-tanstack/Rakefile
  • demos/basic-v16-rspack-tanstack/app/assets/stylesheets/application.css
  • demos/basic-v16-rspack-tanstack/app/controllers/application_controller.rb
  • demos/basic-v16-rspack-tanstack/app/controllers/tanstack_app_controller.rb
  • demos/basic-v16-rspack-tanstack/app/helpers/application_helper.rb
  • demos/basic-v16-rspack-tanstack/app/javascript/packs/application.js
  • demos/basic-v16-rspack-tanstack/app/javascript/packs/server-bundle.js
  • demos/basic-v16-rspack-tanstack/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/HelloWorld/ror_components/HelloWorld.module.css
  • demos/basic-v16-rspack-tanstack/app/javascript/src/HelloWorld/ror_components/HelloWorld.server.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/TanStackApp/ror_components/TanStackApp.client.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/TanStackApp/ror_components/TanStackApp.server.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/router.ts
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/__root.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/about.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/deep.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/index.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/index.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/search.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/$userId.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/index.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/types/css-modules.d.ts
  • demos/basic-v16-rspack-tanstack/app/jobs/application_job.rb
  • demos/basic-v16-rspack-tanstack/app/mailers/application_mailer.rb
  • demos/basic-v16-rspack-tanstack/app/models/application_record.rb
  • demos/basic-v16-rspack-tanstack/app/views/layouts/application.html.erb
  • demos/basic-v16-rspack-tanstack/app/views/layouts/mailer.html.erb
  • demos/basic-v16-rspack-tanstack/app/views/layouts/mailer.text.erb
  • demos/basic-v16-rspack-tanstack/app/views/layouts/tanstack_app.html.erb
  • demos/basic-v16-rspack-tanstack/app/views/pwa/manifest.json.erb
  • demos/basic-v16-rspack-tanstack/app/views/pwa/service-worker.js
  • demos/basic-v16-rspack-tanstack/app/views/tanstack_app/index.html.erb
  • demos/basic-v16-rspack-tanstack/babel.config.js
  • demos/basic-v16-rspack-tanstack/bin/brakeman
  • demos/basic-v16-rspack-tanstack/bin/bundle
  • demos/basic-v16-rspack-tanstack/bin/dev
  • demos/basic-v16-rspack-tanstack/bin/rails
  • demos/basic-v16-rspack-tanstack/bin/rake
  • demos/basic-v16-rspack-tanstack/bin/rubocop
  • demos/basic-v16-rspack-tanstack/bin/setup
  • demos/basic-v16-rspack-tanstack/bin/shakapacker
  • demos/basic-v16-rspack-tanstack/bin/shakapacker-config
  • demos/basic-v16-rspack-tanstack/bin/shakapacker-dev-server
  • demos/basic-v16-rspack-tanstack/bin/shakapacker-precompile-hook
  • demos/basic-v16-rspack-tanstack/bin/switch-bundler
  • demos/basic-v16-rspack-tanstack/bin/thrust
  • demos/basic-v16-rspack-tanstack/config.ru
  • demos/basic-v16-rspack-tanstack/config/application.rb
  • demos/basic-v16-rspack-tanstack/config/boot.rb
  • demos/basic-v16-rspack-tanstack/config/credentials.yml.enc
  • demos/basic-v16-rspack-tanstack/config/database.yml
  • demos/basic-v16-rspack-tanstack/config/environment.rb
  • demos/basic-v16-rspack-tanstack/config/environments/development.rb
  • demos/basic-v16-rspack-tanstack/config/environments/production.rb
  • demos/basic-v16-rspack-tanstack/config/environments/test.rb
  • demos/basic-v16-rspack-tanstack/config/initializers/assets.rb
  • demos/basic-v16-rspack-tanstack/config/initializers/content_security_policy.rb
  • demos/basic-v16-rspack-tanstack/config/initializers/filter_parameter_logging.rb
  • demos/basic-v16-rspack-tanstack/config/initializers/inflections.rb
  • demos/basic-v16-rspack-tanstack/config/initializers/react_on_rails.rb
  • demos/basic-v16-rspack-tanstack/config/initializers/react_on_rails_pro.rb
  • demos/basic-v16-rspack-tanstack/config/locales/en.yml
  • demos/basic-v16-rspack-tanstack/config/puma.rb
  • demos/basic-v16-rspack-tanstack/config/routes.rb
  • demos/basic-v16-rspack-tanstack/config/shakapacker.yml
  • demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js
  • demos/basic-v16-rspack-tanstack/config/webpack/commonWebpackConfig.js
  • demos/basic-v16-rspack-tanstack/config/webpack/development.js
  • demos/basic-v16-rspack-tanstack/config/webpack/generateWebpackConfigs.js
  • demos/basic-v16-rspack-tanstack/config/webpack/production.js
  • demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js
  • demos/basic-v16-rspack-tanstack/config/webpack/test.js
  • demos/basic-v16-rspack-tanstack/config/webpack/webpack.config.js
  • demos/basic-v16-rspack-tanstack/db/seeds.rb
  • demos/basic-v16-rspack-tanstack/docs/react-on-rails-pro-setup.md
  • demos/basic-v16-rspack-tanstack/e2e/tanstack_router.spec.ts
  • demos/basic-v16-rspack-tanstack/package.json
  • demos/basic-v16-rspack-tanstack/playwright.config.ts
  • demos/basic-v16-rspack-tanstack/public/400.html
  • demos/basic-v16-rspack-tanstack/public/404.html
  • demos/basic-v16-rspack-tanstack/public/406-unsupported-browser.html
  • demos/basic-v16-rspack-tanstack/public/422.html
  • demos/basic-v16-rspack-tanstack/public/500.html
  • demos/basic-v16-rspack-tanstack/public/robots.txt
  • demos/basic-v16-rspack-tanstack/react-on-rails-pro/node-renderer.js
  • demos/basic-v16-rspack-tanstack/react-on-rails-pro/package.json
  • demos/basic-v16-rspack-tanstack/test/test_helper.rb
  • demos/basic-v16-rspack-tanstack/tsconfig.json
  • lefthook.yml
✅ Files skipped from review due to trivial changes (1)
  • demos/basic-v16-rspack-tanstack/Procfile.dev
🚧 Files skipped from review as they are similar to previous changes (60)
  • demos/basic-v16-rspack-tanstack/app/views/layouts/mailer.html.erb
  • demos/basic-v16-rspack-tanstack/Procfile.dev-static-assets
  • demos/basic-v16-rspack-tanstack/config/credentials.yml.enc
  • demos/basic-v16-rspack-tanstack/app/javascript/packs/application.js
  • demos/basic-v16-rspack-tanstack/bin/rake
  • demos/basic-v16-rspack-tanstack/config/environment.rb
  • demos/basic-v16-rspack-tanstack/public/500.html
  • demos/basic-v16-rspack-tanstack/public/406-unsupported-browser.html
  • demos/basic-v16-rspack-tanstack/.env.example
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/index.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/router.ts
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/HelloWorld/ror_components/HelloWorld.server.tsx
  • demos/basic-v16-rspack-tanstack/public/robots.txt
  • demos/basic-v16-rspack-tanstack/bin/shakapacker
  • demos/basic-v16-rspack-tanstack/public/404.html
  • demos/basic-v16-rspack-tanstack/app/javascript/src/TanStackApp/ror_components/TanStackApp.client.tsx
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/index.tsx
  • demos/basic-v16-rspack-tanstack/package.json
  • demos/basic-v16-rspack-tanstack/public/422.html
  • demos/basic-v16-rspack-tanstack/app/javascript/packs/server-bundle.js
  • demos/basic-v16-rspack-tanstack/Rakefile
  • demos/basic-v16-rspack-tanstack/bin/shakapacker-dev-server
  • demos/basic-v16-rspack-tanstack/config/initializers/react_on_rails_pro.rb
  • demos/basic-v16-rspack-tanstack/app/jobs/application_job.rb
  • demos/basic-v16-rspack-tanstack/.demo-metadata.yml
  • demos/basic-v16-rspack-tanstack/.dev-services.yml.example
  • demos/basic-v16-rspack-tanstack/config/webpack/generateWebpackConfigs.js
  • demos/basic-v16-rspack-tanstack/app/views/layouts/tanstack_app.html.erb
  • demos/basic-v16-rspack-tanstack/app/views/tanstack_app/index.html.erb
  • demos/basic-v16-rspack-tanstack/app/javascript/src/HelloWorld/ror_components/HelloWorld.module.css
  • demos/basic-v16-rspack-tanstack/app/javascript/src/TanStackApp/ror_components/TanStackApp.server.tsx
  • demos/basic-v16-rspack-tanstack/app/controllers/tanstack_app_controller.rb
  • demos/basic-v16-rspack-tanstack/config/webpack/test.js
  • demos/basic-v16-rspack-tanstack/db/seeds.rb
  • demos/basic-v16-rspack-tanstack/react-on-rails-pro/node-renderer.js
  • demos/basic-v16-rspack-tanstack/app/views/pwa/service-worker.js
  • demos/basic-v16-rspack-tanstack/config/webpack/production.js
  • demos/basic-v16-rspack-tanstack/app/views/layouts/application.html.erb
  • demos/basic-v16-rspack-tanstack/config/environments/test.rb
  • demos/basic-v16-rspack-tanstack/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.tsx
  • demos/basic-v16-rspack-tanstack/app/assets/stylesheets/application.css
  • demos/basic-v16-rspack-tanstack/config.ru
  • demos/basic-v16-rspack-tanstack/app/views/pwa/manifest.json.erb
  • demos/basic-v16-rspack-tanstack/bin/rubocop
  • demos/basic-v16-rspack-tanstack/config/webpack/webpack.config.js
  • demos/basic-v16-rspack-tanstack/config/initializers/inflections.rb
  • demos/basic-v16-rspack-tanstack/app/views/layouts/mailer.text.erb
  • demos/basic-v16-rspack-tanstack/docs/react-on-rails-pro-setup.md
  • demos/basic-v16-rspack-tanstack/public/400.html
  • demos/basic-v16-rspack-tanstack/config/environments/development.rb
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/deep.tsx
  • demos/basic-v16-rspack-tanstack/config/boot.rb
  • demos/basic-v16-rspack-tanstack/config/locales/en.yml
  • demos/basic-v16-rspack-tanstack/bin/shakapacker-config
  • demos/basic-v16-rspack-tanstack/tsconfig.json
  • demos/basic-v16-rspack-tanstack/bin/bundle
  • demos/basic-v16-rspack-tanstack/babel.config.js
  • demos/basic-v16-rspack-tanstack/app/models/application_record.rb
  • demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/$userId.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rb

📄 CodeRabbit inference engine (CLAUDE.md)

When using bin/conductor-exec for Ruby commands (bundle install, bundle exec, etc.), ensure the correct Ruby version from .tool-versions is used

Files:

  • demos/basic-v16-rspack-tanstack/config/initializers/content_security_policy.rb
  • demos/basic-v16-rspack-tanstack/config/initializers/assets.rb
  • demos/basic-v16-rspack-tanstack/config/application.rb
  • demos/basic-v16-rspack-tanstack/config/initializers/filter_parameter_logging.rb
  • demos/basic-v16-rspack-tanstack/app/mailers/application_mailer.rb
  • demos/basic-v16-rspack-tanstack/config/environments/production.rb
  • demos/basic-v16-rspack-tanstack/config/routes.rb
  • demos/basic-v16-rspack-tanstack/config/initializers/react_on_rails.rb
  • demos/basic-v16-rspack-tanstack/config/puma.rb
  • demos/basic-v16-rspack-tanstack/app/controllers/application_controller.rb
  • demos/basic-v16-rspack-tanstack/app/helpers/application_helper.rb
  • demos/basic-v16-rspack-tanstack/test/test_helper.rb
**/*.js

📄 CodeRabbit inference engine (CLAUDE.md)

When using bin/conductor-exec for Node commands (npm install, npm run, etc.), ensure the correct Node version from .tool-versions is used

Files:

  • demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js
  • demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js
  • demos/basic-v16-rspack-tanstack/config/webpack/commonWebpackConfig.js
  • demos/basic-v16-rspack-tanstack/config/webpack/development.js
🧠 Learnings (11)
📚 Learning: 2026-01-15T03:21:00.096Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T03:21:00.096Z
Learning: Applies to config/initializers/react_on_rails.rb : Never overwrite or revert config/initializers/react_on_rails.rb during React on Rails upgrades - this file contains custom settings that must be preserved

Applied to files:

  • demos/basic-v16-rspack-tanstack/config/initializers/content_security_policy.rb
  • demos/basic-v16-rspack-tanstack/config/initializers/assets.rb
  • demos/basic-v16-rspack-tanstack/config/initializers/filter_parameter_logging.rb
  • demos/basic-v16-rspack-tanstack/react-on-rails-pro/package.json
  • demos/basic-v16-rspack-tanstack/config/environments/production.rb
  • demos/basic-v16-rspack-tanstack/bin/shakapacker-precompile-hook
  • demos/basic-v16-rspack-tanstack/bin/dev
  • demos/basic-v16-rspack-tanstack/config/initializers/react_on_rails.rb
  • demos/basic-v16-rspack-tanstack/.gitignore
  • demos/basic-v16-rspack-tanstack/Gemfile
  • demos/basic-v16-rspack-tanstack/README.md
  • demos/basic-v16-rspack-tanstack/.rubocop.yml
📚 Learning: 2026-01-15T03:21:00.096Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T03:21:00.096Z
Learning: Applies to Gemfile : When upgrading Shakapacker or React on Rails, ONLY update version numbers in Gemfile gem version constraints - never run install generators that regenerate config files

Applied to files:

  • demos/basic-v16-rspack-tanstack/config/application.rb
  • demos/basic-v16-rspack-tanstack/bin/switch-bundler
  • demos/basic-v16-rspack-tanstack/bin/shakapacker-precompile-hook
  • demos/basic-v16-rspack-tanstack/bin/dev
  • demos/basic-v16-rspack-tanstack/config/initializers/react_on_rails.rb
  • demos/basic-v16-rspack-tanstack/bin/brakeman
  • demos/basic-v16-rspack-tanstack/Gemfile
  • demos/basic-v16-rspack-tanstack/README.md
📚 Learning: 2026-01-15T03:21:00.096Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T03:21:00.096Z
Learning: Applies to config/webpack/webpack.config.js : Never overwrite or replace config/webpack/webpack.config.js during Shakapacker upgrades - this file contains custom logic that must be preserved

Applied to files:

  • demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js
  • demos/basic-v16-rspack-tanstack/bin/switch-bundler
  • demos/basic-v16-rspack-tanstack/bin/shakapacker-precompile-hook
  • demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js
  • demos/basic-v16-rspack-tanstack/config/shakapacker.yml
  • demos/basic-v16-rspack-tanstack/config/webpack/commonWebpackConfig.js
  • demos/basic-v16-rspack-tanstack/config/initializers/react_on_rails.rb
  • demos/basic-v16-rspack-tanstack/config/webpack/development.js
📚 Learning: 2026-01-15T03:21:00.096Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T03:21:00.096Z
Learning: Applies to config/webpack/*.js : Never overwrite or replace config/webpack/*.js custom configuration files during Shakapacker upgrades - preserve all custom configurations

Applied to files:

  • demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js
  • demos/basic-v16-rspack-tanstack/bin/switch-bundler
  • demos/basic-v16-rspack-tanstack/bin/shakapacker-precompile-hook
  • demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js
  • demos/basic-v16-rspack-tanstack/config/shakapacker.yml
  • demos/basic-v16-rspack-tanstack/config/webpack/commonWebpackConfig.js
  • demos/basic-v16-rspack-tanstack/config/webpack/development.js
📚 Learning: 2026-01-15T03:21:00.096Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T03:21:00.096Z
Learning: When upgrading Shakapacker or React on Rails, read the CHANGELOG for breaking changes and apply only the specific changes mentioned - never use install generators to fix breaking changes

Applied to files:

  • demos/basic-v16-rspack-tanstack/bin/switch-bundler
  • demos/basic-v16-rspack-tanstack/bin/shakapacker-precompile-hook
  • demos/basic-v16-rspack-tanstack/config/initializers/react_on_rails.rb
  • demos/basic-v16-rspack-tanstack/Gemfile
  • demos/basic-v16-rspack-tanstack/README.md
📚 Learning: 2026-01-15T03:21:00.096Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T03:21:00.096Z
Learning: Applies to package.json : When upgrading Shakapacker or React on Rails, ONLY update version numbers in package.json npm package versions - never run install generators that regenerate config files

Applied to files:

  • demos/basic-v16-rspack-tanstack/bin/switch-bundler
  • demos/basic-v16-rspack-tanstack/react-on-rails-pro/package.json
  • demos/basic-v16-rspack-tanstack/bin/shakapacker-precompile-hook
  • demos/basic-v16-rspack-tanstack/config/shakapacker.yml
  • demos/basic-v16-rspack-tanstack/config/initializers/react_on_rails.rb
  • demos/basic-v16-rspack-tanstack/Gemfile
📚 Learning: 2026-01-15T03:21:00.096Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T03:21:00.096Z
Learning: Applies to config/shakapacker.yml : Never overwrite or revert config/shakapacker.yml during Shakapacker upgrades - this file contains project-specific settings that are sacred

Applied to files:

  • demos/basic-v16-rspack-tanstack/bin/switch-bundler
  • demos/basic-v16-rspack-tanstack/config/shakapacker.yml
📚 Learning: 2026-01-15T03:21:00.096Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T03:21:00.096Z
Learning: Never run rails shakapacker:install or rails react_on_rails:install commands during upgrades as they overwrite all custom configurations - only use bundle install and npm install

Applied to files:

  • demos/basic-v16-rspack-tanstack/bin/switch-bundler
  • demos/basic-v16-rspack-tanstack/react-on-rails-pro/package.json
  • demos/basic-v16-rspack-tanstack/bin/setup
  • demos/basic-v16-rspack-tanstack/bin/shakapacker-precompile-hook
  • demos/basic-v16-rspack-tanstack/bin/dev
  • demos/basic-v16-rspack-tanstack/config/initializers/react_on_rails.rb
  • demos/basic-v16-rspack-tanstack/.gitignore
  • demos/basic-v16-rspack-tanstack/Gemfile
  • demos/basic-v16-rspack-tanstack/README.md
📚 Learning: 2026-01-15T03:21:00.096Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T03:21:00.096Z
Learning: Applies to **/*.rb : When using bin/conductor-exec for Ruby commands (bundle install, bundle exec, etc.), ensure the correct Ruby version from .tool-versions is used

Applied to files:

  • demos/basic-v16-rspack-tanstack/bin/switch-bundler
  • lefthook.yml
  • demos/basic-v16-rspack-tanstack/bin/setup
  • demos/basic-v16-rspack-tanstack/bin/rails
  • demos/basic-v16-rspack-tanstack/bin/dev
  • demos/basic-v16-rspack-tanstack/bin/brakeman
📚 Learning: 2026-01-15T03:21:00.096Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T03:21:00.096Z
Learning: When running pre-commit hooks (lefthook) in Conductor, use bin/conductor-exec wrapper script to ensure correct Ruby and Node versions are available

Applied to files:

  • lefthook.yml
  • demos/basic-v16-rspack-tanstack/bin/shakapacker-precompile-hook
  • demos/basic-v16-rspack-tanstack/bin/brakeman
📚 Learning: 2026-01-15T03:21:00.096Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T03:21:00.096Z
Learning: Applies to .tool-versions : When running commands in Conductor, use bin/conductor-exec wrapper script to ensure commands respect .tool-versions for correct Ruby/Node version activation

Applied to files:

  • demos/basic-v16-rspack-tanstack/bin/rails
  • demos/basic-v16-rspack-tanstack/bin/brakeman
🧬 Code graph analysis (12)
demos/basic-v16-rspack-tanstack/config/initializers/assets.rb (1)
demos/basic-v16-rspack-tanstack/config/application.rb (1)
  • config (22-41)
demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js (3)
demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js (2)
  • require (4-4)
  • commonWebpackConfig (5-5)
demos/basic-v16-rspack-tanstack/config/webpack/commonWebpackConfig.js (2)
  • require (5-5)
  • commonWebpackConfig (16-16)
demos/basic-v16-rspack-tanstack/config/webpack/generateWebpackConfigs.js (1)
  • clientConfig (8-8)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/about.tsx (8)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/__root.tsx (1)
  • Route (4-8)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/search.tsx (1)
  • Route (9-22)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/deep.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/$userId.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/config/initializers/filter_parameter_logging.rb (1)
demos/basic-v16-rspack-tanstack/config/application.rb (1)
  • config (22-41)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/__root.tsx (8)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/about.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/search.tsx (1)
  • Route (9-22)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/deep.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/$userId.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/search.tsx (8)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/__root.tsx (1)
  • Route (4-8)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/about.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/deep.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/demo/nested/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/$userId.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/index.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/config/environments/production.rb (1)
demos/basic-v16-rspack-tanstack/config/application.rb (1)
  • config (22-41)
demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js (5)
demos/basic-v16-rspack-tanstack/react-on-rails-pro/node-renderer.js (2)
  • require (17-19)
  • config (21-39)
demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js (4)
  • require (6-6)
  • require (29-29)
  • require (37-37)
  • commonWebpackConfig (5-5)
demos/basic-v16-rspack-tanstack/config/webpack/commonWebpackConfig.js (2)
  • require (5-5)
  • commonWebpackConfig (16-16)
demos/basic-v16-rspack-tanstack/config/webpack/development.js (1)
  • require (4-4)
demos/basic-v16-rspack-tanstack/config/webpack/generateWebpackConfigs.js (1)
  • serverWebpackConfig (5-5)
demos/basic-v16-rspack-tanstack/config/routes.rb (1)
packages/shakacode_demo_common/lib/shakacode_demo_common.rb (1)
  • root (10-12)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/index.tsx (1)
demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/$userId.tsx (1)
  • Route (3-5)
demos/basic-v16-rspack-tanstack/config/initializers/react_on_rails.rb (2)
demos/basic-v16-rspack-tanstack/react-on-rails-pro/node-renderer.js (1)
  • config (21-39)
demos/basic-v16-rspack-tanstack/config/application.rb (1)
  • config (22-41)
demos/basic-v16-rspack-tanstack/config/webpack/development.js (8)
demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js (1)
  • require (4-4)
demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js (3)
  • require (6-6)
  • require (29-29)
  • require (37-37)
demos/basic-v16-rspack-tanstack/config/webpack/commonWebpackConfig.js (1)
  • require (5-5)
demos/basic-v16-rspack-tanstack/config/webpack/webpack.config.js (3)
  • require (1-1)
  • require (2-2)
  • require (3-3)
demos/basic-v16-rspack-tanstack/config/webpack/production.js (1)
  • generateWebpackConfigs (4-4)
demos/basic-v16-rspack-tanstack/config/webpack/test.js (1)
  • generateWebpackConfigs (4-4)
demos/basic-v16-rspack-tanstack/config/webpack/generateWebpackConfigs.js (1)
  • clientWebpackConfig (4-4)
demos/basic-v16-rspack-tanstack/config/application.rb (1)
  • config (22-41)
🪛 Checkov (3.2.334)
demos/basic-v16-rspack-tanstack/config/database.yml

[medium] 68-69: Basic Auth Credentials

(CKV_SECRET_4)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: claude-review
🔇 Additional comments (62)
demos/basic-v16-rspack-tanstack/app/helpers/application_helper.rb (1)

1-4: LGTM!

Standard Rails helper module boilerplate with the frozen_string_literal pragma. This is idiomatic Rails scaffolding that provides a placeholder for view helpers to be added as needed.

demos/basic-v16-rspack-tanstack/test/test_helper.rb (1)

1-15: LGTM!

This is a standard Rails test helper with appropriate configuration. The parallel test setup using :number_of_processors and fixture loading follows Rails conventions and is well-suited for the demo application.

demos/basic-v16-rspack-tanstack/app/controllers/application_controller.rb (1)

1-5: LGTM!

The ApplicationController follows Rails 8 conventions with appropriate browser version restrictions. The explicit CSRF protection with protect_from_forgery with: :exception addresses the previous security concern and ensures robust defense against cross-site request forgery attacks.

demos/basic-v16-rspack-tanstack/app/mailers/application_mailer.rb (1)

1-4: LGTM!

Standard Rails ApplicationMailer scaffold code. The placeholder email address is appropriate for a demo application.

demos/basic-v16-rspack-tanstack/app/javascript/types/css-modules.d.ts (1)

1-15: LGTM!

This is a standard and correct TypeScript declaration file for CSS modules. The type definitions properly map class names to their generated unique string identifiers, and the coverage of .css, .scss, and .sass extensions is comprehensive for the project's needs.

demos/basic-v16-rspack-tanstack/config/database.yml (1)

15-85: LGTM! Standard Rails database configuration with proper credential handling.

The production password is correctly sourced from an environment variable, and development/test configurations appropriately rely on peer authentication.

Regarding the static analysis hint (CKV_SECRET_4) on lines 68-69: this is a false positive — the flagged postgres://myuser:mypass@localhost/somedatabase is simply an example URL in documentation comments, not an actual credential.

demos/basic-v16-rspack-tanstack/bin/thrust (1)

1-5: LGTM!

This is a standard Rails 8 bin stub for thruster (HTTP/2 proxy and asset compression). The pattern correctly uses Bundler setup and delegates to the gem binary via Gem.bin_path.

demos/basic-v16-rspack-tanstack/config/initializers/filter_parameter_logging.rb (1)

6-8: LGTM!

This is the standard Rails 8 parameter filtering configuration. The comprehensive list appropriately covers authentication credentials, encryption-related values, and PII/financial data (including :email for GDPR/CCPA compliance).

demos/basic-v16-rspack-tanstack/config/initializers/content_security_policy.rb (1)

1-25: LGTM — Standard Rails CSP template appropriate for a demo app.

The entirely commented-out CSP initializer is reasonable for a demo project, allowing developers to focus on learning TanStack Router integration without CSP-related friction. The template itself follows best practices (no unsafe-inline/unsafe-eval) and can be uncommented when needed.

demos/basic-v16-rspack-tanstack/config/puma.rb (1)

1-38: LGTM! Standard Rails Puma configuration.

This is a well-documented, conventional Puma configuration appropriate for the demo application. The environment-variable-driven settings (threads, port, pidfile) provide good flexibility for different deployment scenarios, and the comments clearly explain the threading model and GVL considerations.

demos/basic-v16-rspack-tanstack/bin/shakapacker-precompile-hook (2)

1-15: LGTM on the script header and documentation.

The shebang, frozen_string_literal pragma, and comprehensive documentation block clearly explain the script's purpose and integration with Shakapacker's precompile_hook configuration.


16-21: LGTM on environment setup.

Setting REACT_ON_RAILS_SKIP_VALIDATION before loading the Rails environment is appropriate, and the rationale is well-documented.

demos/basic-v16-rspack-tanstack/react-on-rails-pro/package.json (1)

1-10: Package structure is appropriate for the SSR node renderer setup.

The minimal package.json is fit for purpose with the RC version intentionally used for showcasing React on Rails 16.2.0 features. No changes needed.

demos/basic-v16-rspack-tanstack/bin/switch-bundler (7)

10-18: LGTM!

The dependency constants are well-organized and properly frozen.


20-24: LGTM!

Good fail-fast pattern with immediate validation and proper input normalization.


26-37: LGTM!

Clear orchestration with helpful user feedback and actionable next steps.


41-45: LGTM!

Validation is correct and provides a clear error message.


47-62: Good use of YAML.safe_load.

The security concern from the previous review has been properly addressed. The method now safely parses the YAML file with explicit permitted classes.


89-122: LGTM!

Excellent use of the array form of system() to prevent command injection vulnerabilities. Error handling with explicit return value checks is also well implemented.


132-141: LGTM!

Clean CLI entrypoint with helpful usage instructions and proper exit code handling.

demos/basic-v16-rspack-tanstack/bin/rails (1)

1-4: LGTM!

Standard Rails bin/rails entrypoint with correct shebang, APP_PATH configuration, and boot sequence.

demos/basic-v16-rspack-tanstack/Procfile.dev-prod-assets (1)

1-8: LGTM!

Clear Procfile for testing production assets in development. Port 3001 avoids conflicts with the standard dev server, and commented examples provide useful reference for optional services.

demos/basic-v16-rspack-tanstack/config/initializers/assets.rb (1)

1-7: LGTM!

Standard Rails asset pipeline initializer. The version string allows cache busting when needed.

demos/basic-v16-rspack-tanstack/README.md (1)

1-236: Excellent documentation!

Comprehensive README with clear setup instructions, thorough SSR configuration explanation (including why Node renderer is required), and honest limitations section. The tables for generated files and scripts are particularly helpful.

demos/basic-v16-rspack-tanstack/.gitignore (1)

1-39: LGTM!

Well-organized .gitignore with appropriate patterns for:

  • Generated assets (public/packs, ssr-generated) that should be built during deployment
  • TanStack Router auto-generated route tree
  • Security-sensitive files (credentials, master key, .npmrc with auth tokens)
  • Test artifacts

The .env.example exception is good practice for documenting required environment variables.

demos/basic-v16-rspack-tanstack/bin/dev (1)

1-26: LGTM!

The development server script is well-structured with clear documentation of the three Procfile modes. The frozen_string_literal pragma and bundler setup are correctly placed before loading the ReactOnRails dev module.

demos/basic-v16-rspack-tanstack/bin/setup (1)

1-37: LGTM!

The setup script follows standard Rails conventions with idempotent operations. Good practices observed:

  • system! wrapper with exception: true for proper error handling
  • bundle check || system!("bundle install") avoids unnecessary reinstalls
  • STDOUT.flush before exec ensures output is displayed
  • --skip-server flag provides flexibility for CI/automation scenarios
demos/basic-v16-rspack-tanstack/config/initializers/react_on_rails.rb (1)

1-76: LGTM!

The React on Rails configuration is well-documented with clear explanations of each option. Key highlights:

  • enforce_private_server_bundles = true ensures server-side code isn't exposed publicly
  • Comments clearly document the React on Rails Pro dependency for TanStack Router SSR
  • Configuration aligns with the node-renderer.js setup (ssr-generated bundle path)

Based on learnings, this file contains custom settings that must be preserved during upgrades—the thorough documentation will help future maintainers understand each setting's purpose.

demos/basic-v16-rspack-tanstack/config/application.rb (1)

1-42: LGTM!

Standard Rails 8 application configuration with appropriate selective loading of railties. The decision to disable system test generation (config.generators.system_tests = nil) aligns well with the PR's use of Playwright for E2E testing.

demos/basic-v16-rspack-tanstack/config/environments/production.rb (1)

1-86: LGTM!

Solid Rails 8 production configuration with good security and operational practices:

  • SSL enforcement with force_ssl and assume_ssl
  • 12-factor compliant logging to STDOUT with request ID tagging
  • attributes_for_inspect = [:id] prevents PII leakage in logs
  • Health check path silencing reduces log noise

The example.com placeholder on line 57 is acceptable for a demo application.

demos/basic-v16-rspack-tanstack/config/routes.rb (1)

3-16: LGTM! Routes correctly support SSR for TanStack Router SPA.

The routing pattern is appropriate for a single-page application with server-side rendering—all client routes map to the same controller action which renders the initial HTML. The :userId camelCase parameter on line 11 aligns with TanStack Router's frontend conventions.

demos/basic-v16-rspack-tanstack/config/webpack/commonWebpackConfig.js (2)

15-18: Good use of function factory to avoid mutating shared globals.

Returning a fresh merged config on each call prevents unintended side effects from modifying the base config. This pattern is essential given the mutable nature of the webpack config objects.


9-13: The extensions array is not problematic — webpack-merge concatenates rather than replaces arrays.

Shakapacker's merge function re-exports webpack-merge, which concatenates arrays when merging configs. The generateWebpackConfig() provides webpack's default extensions ([".js", ".json", ".wasm"]), and when merged with commonOptions, the result includes all extensions: [".js", ".json", ".wasm", ".css", ".ts", ".tsx"]. The .js files in the project (e.g., application.js, server-bundle.js) will resolve correctly.

Likely an incorrect or invalid review comment.

demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js (1)

8-45: LGTM! Clean conditional plugin loading with appropriate error handling.

The implementation correctly:

  • Removes the server-bundle entry to prevent client-side loading issues
  • Conditionally loads the appropriate TanStack Router plugin based on the bundler type
  • Uses try/catch with warnings for graceful degradation if the plugin isn't available
demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js (1)

122-132: LGTM! SSR-specific settings are appropriate.

Using devtool: 'eval' for fast SSR debugging and target: 'node' for Node.js APIs are correct choices for a server bundle intended for React on Rails Pro's Node renderer.

demos/basic-v16-rspack-tanstack/config/shakapacker.yml (1)

28-63: LGTM! Development configuration is appropriate for the demo setup.

The compile: false setting in development is correct since this demo uses a separate webpack dev server (as indicated by the dev_server configuration). HMR and inline CSS settings are properly configured.

demos/basic-v16-rspack-tanstack/config/webpack/development.js (1)

8-26: LGTM! React Fast Refresh setup is well-structured.

The conditional logic correctly differentiates between rspack and webpack bundlers, loading the appropriate refresh plugin for each. The guard on WEBPACK_SERVE ensures the plugin is only added during dev server operation, which is the correct behavior for HMR.

demos/basic-v16-rspack-tanstack/Gemfile (1)

1-54: LGTM! Well-structured Gemfile for the Rails 8 demo.

The gem selection is appropriate for the demo's purpose. The use of react_on_rails release candidate (16.2.0.rc1) is acceptable for a demo app showcasing upcoming features.

demos/basic-v16-rspack-tanstack/bin/brakeman (1)

1-7: LGTM!

Standard Rails bin script pattern for Brakeman. The --ensure-latest flag is a good practice to encourage keeping security scanning tools up to date.

demos/basic-v16-rspack-tanstack/.rubocop.yml (1)

1-8: LGTM!

Clean RuboCop configuration inheriting from Rails Omakase. The commented example provides helpful guidance for future customization.

demos/basic-v16-rspack-tanstack/app/javascript/src/routes/users/index.tsx (2)

3-5: LGTM! Route definition follows TanStack Router conventions.

The file-based routing setup using createFileRoute is correctly implemented and consistent with the project's other route files (e.g., /users/$userId).


33-35: Good use of type-safe route parameters.

The Link correctly converts user.id to a string for the route params, which aligns with TanStack Router's expectation that URL parameters are strings.

demos/basic-v16-rspack-tanstack/e2e/tanstack_router.spec.ts (5)

5-14: LGTM!

The home page SSR test correctly validates that server-rendered content is visible after domcontentloaded, which is appropriate for SSR verification.


16-24: LGTM!

Clean SSR verification for the about page.


26-57: LGTM!

Good coverage of SSR for URL parameters, search params, and nested routes. The specific h3:text("Deep Nested Page") selector appropriately avoids matching navigation links.


60-120: LGTM!

Client hydration tests properly wait for elements to be visible before interaction, avoiding flakiness. The navigation tests verify both URL updates and content changes.


122-151: LGTM!

The hydration consistency test uses a smart approach: verifying navigation works confirms React hydration completed, which is more reliable than arbitrary timeouts. The console log filtering for hydration-related messages is appropriate.

demos/basic-v16-rspack-tanstack/app/javascript/src/routes/about.tsx (2)

1-5: LGTM!

The route definition follows the established TanStack Router file-based routing pattern used consistently across other routes in this demo.


7-38: LGTM!

Clean, informative About page component with appropriate static content describing the demo's technology stack and SSR behavior.

demos/basic-v16-rspack-tanstack/app/javascript/src/routes/search.tsx (3)

9-22: LGTM!

The validateSearch function correctly handles various input types with appropriate fallbacks. The parseInt(search.page, 10) || 1 pattern properly handles NaN from non-numeric strings.


24-48: LGTM!

The component logic is well-structured. The useEffect properly syncs the input value with URL changes (e.g., browser back/forward), and navigation handlers correctly manage search state.


49-119: LGTM!

The JSX renders a complete search UI with form, current state display, pagination controls, and helpful example URLs. The pagination buttons correctly disable "Previous" when on page 1.

demos/basic-v16-rspack-tanstack/playwright.config.ts (2)

3-15: LGTM!

Sensible Playwright configuration. Serial execution with 1 worker is appropriate for avoiding race conditions with server resources. The CI-aware retry and forbidOnly settings are good practices.


24-33: LGTM!

The webServer configuration appropriately handles both development (reuseExistingServer: true) and CI (reuseExistingServer: false) scenarios. The SKIP_WEB_SERVER escape hatch is useful for running tests against an already-running server.

lefthook.yml (2)

54-88: LGTM!

ESLint and Prettier hooks correctly handle per-demo execution with appropriate manual git add calls after fixes. The || true after grep prevents hook failure when no demo files are staged.


25-51: [Your rewritten review comment text here]
[Exactly ONE classification tag]

demos/basic-v16-rspack-tanstack/app/javascript/src/routes/__root.tsx (6)

1-8: LGTM! Clean root route setup.

The route configuration properly establishes the TanStack Router root with error handling and 404 components. The function hoisting allows referencing components before their declarations.


14-33: Good error boundary implementation.

The error component maintains navigation consistency and properly delegates error rendering to TanStack Router's ErrorComponent. This ensures users can navigate away from error states.


38-60: Clean 404 implementation.

The not-found component provides clear user feedback with consistent navigation and a direct link back to home. The visual distinction (amber theme) appropriately signals a warning state.


62-68: Appropriate use of shared style objects.

The as const assertion on fontWeight correctly narrows the type for TypeScript. Style sharing avoids duplication across link components.


74-125: Well-structured navigation with good accessibility.

The navigation properly implements:

  • aria-label on the nav element for screen readers
  • aria-current="page" via activeProps for active link identification
  • activeOptions={{ exact: true }} on Home to prevent it being highlighted on all routes

The parent routes (Users, Nested Routes) will correctly highlight when viewing child routes, which is typically the expected UX for hierarchical navigation.


127-136: LGTM! Standard root layout pattern.

The component correctly uses Outlet to render child route content, maintaining a consistent structure with the error and not-found components.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

- Replace weak example password with CHANGE_THIS_TO_A_STRONG_PASSWORD
- Remove outdated GitHub PAT reference from .env.example
- Add trailing newline to package.json output in switch-bundler
- Add null check before accessing rule.use.options in serverWebpackConfig
- Fix Created date from 2025 to 2026 in README

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@claude
Copy link

claude bot commented Jan 15, 2026

Code Review: TanStack Router Demo with SSR and TypeScript

This is an excellent addition to the demos repository! The PR demonstrates a well-structured implementation of TanStack Router with React on Rails SSR support. Here's my detailed review:


Strengths

1. Excellent Code Quality

  • Clean Architecture: The separation between client and server components is clear and well-documented
  • TypeScript Integration: Proper type definitions throughout, including the router configuration
  • Documentation: Comprehensive inline comments explaining SSR limitations and design decisions
  • Commit History: Well-structured, incremental commits with clear messages and co-authorship attribution

2. Security Best Practices

  • Command Injection Prevention: The bin/switch-bundler script properly uses array form for system calls (line 100, 104, 112, 116) to prevent command injection
  • Password Security: .env.example uses CHANGE_THIS_TO_A_STRONG_PASSWORD placeholder instead of weak example passwords
  • Gitignore Configuration: Properly excludes sensitive files (.env*, react-on-rails-pro/.npmrc, credentials)
  • Database Credentials: Uses environment variables for production database passwords

3. Robust Test Coverage

The E2E tests in e2e/tanstack_router.spec.ts are comprehensive and well-designed:

  • ✅ SSR verification across multiple route types (params, search params, nested routes)
  • ✅ Client hydration testing with proper wait strategies
  • ✅ Hydration mismatch detection
  • ✅ Smart use of domcontentloaded instead of flaky networkidle (excellent choice for HMR environments)

4. Configuration Management

  • Dual Bundler Support: Both Webpack and Rspack configurations in clientWebpackConfig.js (lines 27-42)
  • Proper Error Handling: Try-catch blocks with helpful warnings when plugins aren't available
  • SSR Target Configuration: Correctly sets target: 'node' for server bundles

5. Documentation Excellence

  • Clear README: Comprehensive setup instructions, feature explanations, and limitation disclosures
  • SSR Limitations Documented: Honest disclosure about async loader limitations and workarounds
  • React on Rails Pro Requirement: Clear explanation of why Pro is required (Node.js APIs)

🎯 Areas for Consideration (Minor Improvements)

1. Test Robustness

File: e2e/tanstack_router.spec.ts:139-150

The hydration mismatch test relies on clicking a link to verify hydration completion. Consider adding an explicit wait for React hydration:

// More explicit hydration check
await page.waitForFunction(() => {
  const rootElement = document.querySelector('[data-reactroot]');
  return rootElement && rootElement._reactRootContainer !== undefined;
});

Impact: Low - Current approach works but could be more explicit

2. Configuration Warnings

File: config/webpack/serverWebpackConfig.js:65-72

The warning for missing private_output_path is helpful, but since this is a demo app with the config properly set, this code path shouldn't execute. Consider:

  • Promoting this to an error in strict mode
  • Adding a test to verify the configuration is present

Impact: Very Low - Defensive programming is good practice

3. CSS Module Configuration

File: config/webpack/serverWebpackConfig.js:100-111

The CSS loader configuration for SSR has nested conditionals checking for css-loader. Consider extracting this logic to a helper function for clarity:

function configureCSSLoaderForSSR(rule) {
  if (!Array.isArray(rule.use)) return;
  
  const cssLoader = findCSSLoader(rule.use);
  if (cssLoader?.options) {
    cssLoader.options.modules = { exportOnlyLocals: true };
  }
}

Impact: Very Low - Current code works correctly

4. Null Safety

File: config/webpack/serverWebpackConfig.js:114-120

Good null checks on rule.use, but line 118 could benefit from additional safety:

if (rule.use?.options) {
  rule.use.options.emitFile = false;
} else if (rule.use) {
  rule.use.options = { emitFile: false };
}

Impact: Very Low - Unlikely edge case


📊 Test Coverage Assessment

Excellent Coverage:

  • ✅ Server-side rendering verification
  • ✅ URL parameter handling
  • ✅ Search parameter handling
  • ✅ Nested route rendering
  • ✅ Client-side navigation
  • ✅ Hydration consistency

Potential Additions (Optional):

  • Error boundary testing
  • 404 route handling
  • Edge cases (malformed URLs, invalid params)
  • Performance metrics (SSR render time)

🔒 Security Review: PASSED

No security concerns identified. The code follows best practices:

  • ✅ No hardcoded secrets or credentials
  • ✅ Proper command injection prevention
  • ✅ Environment variable usage for sensitive config
  • ✅ Appropriate .gitignore exclusions

📝 Performance Considerations

Positive:

  • Rspack: Significantly faster builds than Webpack
  • SWC Transpiler: 20x faster than Babel (as documented)
  • Code Splitting: TanStack Router's autoCodeSplitting: true enabled

Note:

The synchronous SSR approach (void router.load()) is appropriate for this demo. For production apps with data-heavy routes, consider:

  • React on Rails Pro streaming SSR
  • Client-side data fetching with skeleton screens
  • Rails controller prop injection for critical data

🎓 Adherence to CLAUDE.md Guidelines

Excellent compliance with repository conventions:

  • Version updates in Gemfile and package.json only (no config overwrites)
  • Custom webpack configurations preserved and enhanced
  • No use of destructive install commands
  • Clear documentation about React on Rails Pro dependency
  • Proper use of bin/conductor-exec patterns acknowledged in hook fixes

🏆 Final Assessment

Overall Rating: ⭐⭐⭐⭐⭐ Excellent

This PR demonstrates:

  • Production-quality code with excellent documentation
  • Comprehensive test coverage
  • Strong security practices
  • Clear architectural decisions
  • Proper version management

Recommendation: APPROVE

The minor suggestions above are purely optional refinements. The code is ready to merge as-is and serves as an excellent reference implementation for TanStack Router with React on Rails SSR.


🙏 Additional Kudos

Special appreciation for:

  1. The iterative commit history showing real development process
  2. Addressing CodeRabbit feedback in commit a2893a1
  3. Comprehensive README with clear limitation disclosures
  4. Excellent inline documentation explaining "why" not just "what"
  5. RuboCop compliance and pre-commit hook improvements

Great work! 🎉

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js`:
- Around line 63-72: The warning text is misleading because the code only checks
config.privateOutputPath rather than detecting Shakapacker's version; update the
logic so the message is version-agnostic (or add actual version detection).
Specifically, change the console.warn block that runs when
config.privateOutputPath is falsy (referencing config.privateOutputPath) to
remove the "Shakapacker 9.0+ detected" claim and instead explain that
private_output_path is not configured and must be added (or, if you prefer to
detect the version, implement a real check for Shakapacker version and
conditionally show the 9.0+ wording).
🧹 Nitpick comments (1)
demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js (1)

29-38: Remove duplicate CSS plugin filtering logic.

This loop only handles string items and is redundant with the more comprehensive filtering at lines 88-98, which handles both string and object loader items. Consider removing this block to avoid confusion and reduce maintenance burden.

♻️ Suggested removal
-  // Remove the mini-css-extract-plugin from the style loaders because
-  // the client build will handle exporting CSS.
-  // replace file-loader with null-loader
-  serverWebpackConfig.module.rules.forEach((loader) => {
-    if (loader.use && loader.use.filter) {
-      loader.use = loader.use.filter(
-        (item) => !(typeof item === 'string' && item.match(/mini-css-extract-plugin/))
-      );
-    }
-  });
-
   // No splitting of chunks for a server bundle
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 72b4250 and a2893a1.

📒 Files selected for processing (4)
  • demos/basic-v16-rspack-tanstack/.env.example
  • demos/basic-v16-rspack-tanstack/README.md
  • demos/basic-v16-rspack-tanstack/bin/switch-bundler
  • demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • demos/basic-v16-rspack-tanstack/.env.example
  • demos/basic-v16-rspack-tanstack/bin/switch-bundler
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

📄 CodeRabbit inference engine (CLAUDE.md)

When using bin/conductor-exec for Node commands (npm install, npm run, etc.), ensure the correct Node version from .tool-versions is used

Files:

  • demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js
🧠 Learnings (6)
📚 Learning: 2026-01-15T03:21:00.096Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T03:21:00.096Z
Learning: Applies to config/webpack/webpack.config.js : Never overwrite or replace config/webpack/webpack.config.js during Shakapacker upgrades - this file contains custom logic that must be preserved

Applied to files:

  • demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js
📚 Learning: 2026-01-15T03:21:00.096Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T03:21:00.096Z
Learning: Applies to config/webpack/*.js : Never overwrite or replace config/webpack/*.js custom configuration files during Shakapacker upgrades - preserve all custom configurations

Applied to files:

  • demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js
📚 Learning: 2026-01-15T03:21:00.096Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T03:21:00.096Z
Learning: When upgrading Shakapacker or React on Rails, read the CHANGELOG for breaking changes and apply only the specific changes mentioned - never use install generators to fix breaking changes

Applied to files:

  • demos/basic-v16-rspack-tanstack/README.md
📚 Learning: 2026-01-15T03:21:00.096Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T03:21:00.096Z
Learning: Never run rails shakapacker:install or rails react_on_rails:install commands during upgrades as they overwrite all custom configurations - only use bundle install and npm install

Applied to files:

  • demos/basic-v16-rspack-tanstack/README.md
📚 Learning: 2026-01-15T03:21:00.096Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T03:21:00.096Z
Learning: Applies to config/initializers/react_on_rails.rb : Never overwrite or revert config/initializers/react_on_rails.rb during React on Rails upgrades - this file contains custom settings that must be preserved

Applied to files:

  • demos/basic-v16-rspack-tanstack/README.md
📚 Learning: 2026-01-15T03:21:00.096Z
Learnt from: CR
Repo: shakacode/react_on_rails-demos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T03:21:00.096Z
Learning: Applies to Gemfile : When upgrading Shakapacker or React on Rails, ONLY update version numbers in Gemfile gem version constraints - never run install generators that regenerate config files

Applied to files:

  • demos/basic-v16-rspack-tanstack/README.md
🧬 Code graph analysis (1)
demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js (4)
demos/basic-v16-rspack-tanstack/config/webpack/clientWebpackConfig.js (4)
  • require (6-6)
  • require (29-29)
  • require (37-37)
  • commonWebpackConfig (5-5)
demos/basic-v16-rspack-tanstack/react-on-rails-pro/node-renderer.js (2)
  • require (17-19)
  • config (21-39)
demos/basic-v16-rspack-tanstack/config/webpack/commonWebpackConfig.js (2)
  • require (5-5)
  • commonWebpackConfig (16-16)
demos/basic-v16-rspack-tanstack/config/webpack/generateWebpackConfigs.js (1)
  • serverWebpackConfig (5-5)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: claude-review
🔇 Additional comments (7)
demos/basic-v16-rspack-tanstack/README.md (1)

1-236: Excellent comprehensive documentation!

This README is exceptionally well-structured and thorough. It clearly documents:

  • React on Rails Pro requirements and Node renderer necessity
  • Complete setup and build instructions
  • SSR configuration details with code examples
  • Important limitations (route synchronization, synchronous SSR)
  • Deployment considerations
  • Comprehensive reference tables for scripts, files, and generated directories

The explanations of why the Node renderer is required (lines 115-125) and the limitations around route synchronization (lines 181-196) are particularly valuable for users. The documentation will help developers understand both the capabilities and constraints of this integration.

demos/basic-v16-rspack-tanstack/config/webpack/serverWebpackConfig.js (6)

1-8: LGTM!

Clean imports with good conditional bundler selection pattern supporting both Rspack and Webpack.


9-27: LGTM!

Good defensive coding using merge to avoid mutating the shared global config, and clear error handling when the server-bundle entry is missing.


40-44: LGTM!

Appropriate optimization settings for an SSR bundle—disabling minimization and limiting to a single chunk.


74-81: LGTM!

Appropriate plugin filtering for SSR—removing manifest generation, CSS extraction, and TypeScript checking that belong in the client build.


83-121: LGTM!

Previously flagged issues have been properly addressed:

  • testValue initialized to '' to prevent TypeErrors (lines 92, 101)
  • Null check for rule.use.options added (line 118)

The loader rule configuration correctly handles CSS modules for SSR and disables file emission for asset loaders.


123-136: LGTM!

Appropriate devtool and target settings for SSR. The documentation clearly explains why Node.js target is required for TanStack Router.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

The warning message incorrectly claimed to detect Shakapacker 9.0+ when
it only checks if privateOutputPath is undefined. Updated to be
version-agnostic and clarify it's using a fallback path.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@claude
Copy link

claude bot commented Jan 15, 2026

Pull Request Review: TanStack Router Demo with SSR

Excellent work on this comprehensive demo! This is a well-structured implementation showcasing TanStack Router with React on Rails. Below are my findings organized by category:


✅ Strengths

Code Quality & Architecture

  • Excellent separation of concerns: Clear distinction between client and server components (TanStackApp.client.tsx vs TanStackApp.server.tsx)
  • Well-documented limitations: The SSR component clearly documents async loader limitations with actionable alternatives
  • Type safety: Good TypeScript integration with proper type declarations
  • Comprehensive E2E tests: Playwright tests thoroughly cover SSR, hydration, and routing scenarios
  • Clean webpack configuration: Separate client/server configs with shared common config

Security

  • ✅ Command injection prevention: bin/switch-bundler uses array form for system calls (lines 100, 104, 112, 116)
  • ✅ Password handling: Default password is placeholder (CHANGE_THIS_TO_A_STRONG_PASSWORD in .env.example)
  • ✅ Environment variable usage: Sensitive config properly uses ENV vars
  • ✅ No hardcoded secrets: All credentials are externalized

Documentation

  • Outstanding README: Comprehensive setup instructions, clear limitations section, well-organized
  • Inline documentation: Excellent comments explaining non-obvious decisions (e.g., void router.load() explanation)
  • Setup guide: Dedicated React on Rails Pro setup documentation

🐛 Potential Issues

1. Node Renderer Package Reference (Minor)

File: react-on-rails-pro/node-renderer.js:19

The import references @shakacode-tools/react-on-rails-pro-node-renderer:

const {
  reactOnRailsProNodeRenderer,
} = require('@shakacode-tools/react-on-rails-pro-node-renderer');

However, commit message (72b4250) indicates migration to regular npm packages. Verify this package name is correct or should be updated to match the new registry structure.

2. Null Safety in serverWebpackConfig.js (Minor)

File: config/webpack/serverWebpackConfig.js:108

The code checks cssLoader && cssLoader.options but doesn't verify the options property exists before accessing nested properties:

if (cssLoader && cssLoader.options) {
  cssLoader.options.modules = { exportOnlyLocals: true };
}

While this is likely safe in practice, consider:

if (cssLoader && cssLoader.options) {
  cssLoader.options.modules = cssLoader.options.modules || {};
  cssLoader.options.modules.exportOnlyLocals = true;
}

3. Missing Error Handling in TanStack Router Plugin (Low Priority)

File: config/webpack/clientWebpackConfig.js:27-42

Try-catch blocks warn but don't provide recovery instructions:

catch (e) {
  console.warn('TanStack Router Rspack plugin not available:', e.message);
}

Consider adding installation hints or failing loudly if the plugin is required for this demo to function.

4. Playwright Test Selector Specificity (Minor)

File: e2e/tanstack_router.spec.ts:56, 118

Using h3:text("Deep Nested Page") is fragile if text changes. Consider data-testid attributes:

await expect(page.locator('[data-testid="deep-nested-heading"]')).toBeVisible();

⚡ Performance Considerations

Positive

  • ✅ Rspack bundler: Excellent choice for fast builds (20x faster than Babel mentioned in commits)
  • ✅ SWC transpiler: Much faster than Babel
  • ✅ Code splitting: autoCodeSplitting: true enabled for TanStack Router
  • ✅ Preload optimization: defaultPreload: 'intent' balances performance and UX

Suggestions

  1. Minimize source maps in production: devtool: 'eval' is great for development but ensure production uses optimized source maps
  2. Worker concurrency: Node renderer defaults to 3 workers, which is good. CI reduction to 2 is smart.
  3. Consider bundle analysis: Add webpack-bundle-analyzer to identify optimization opportunities

🔒 Security Review

Critical Security Checks: ✅ All Passed

  • ✅ No SQL injection vectors (no raw queries)
  • ✅ No XSS vulnerabilities (React escapes by default, no dangerouslySetInnerHTML)
  • ✅ Command injection prevented (array form for system calls)
  • ✅ Credentials externalized
  • ✅ CSRF protection enabled (protect_from_forgery with: :exception)

Recommendations

  1. Production password strength: Ensure deployment documentation emphasizes strong RENDERER_PASSWORD
  2. Content Security Policy: The initializer exists but consider configuring it for production
  3. Database credentials: Production uses ENV vars correctly

📊 Test Coverage

Excellent Coverage

  • ✅ SSR verification (home, about, URL params, search params, nested routes)
  • ✅ Client hydration (navigation, forms, state updates)
  • ✅ Hydration consistency (no mismatch warnings)
  • ✅ Proper use of domcontentloaded instead of networkidle (better with HMR)

Suggestions

  1. Consider adding error state tests (404 routes, invalid params)
  2. Test accessibility (a11y checks with @axe-core/playwright)
  3. Add visual regression tests for critical routes

🎯 Adherence to CLAUDE.md Guidelines

✅ Excellent Compliance

  • Upgrade safety: This is a new demo, not an upgrade, so destructive install commands aren't a concern
  • Configuration preservation: Custom webpack configs are intentional and documented
  • Local dependencies: No local gem paths committed
  • mise/asdf awareness: Uses bin/mise-exec pattern (though this demo doesn't include it, the root does)

📝 Minor Suggestions

  1. README date: Already corrected to 2026 ✅
  2. Trailing newlines: package.json already has trailing newline ✅
  3. RuboCop autocorrect: Already added --autocorrect flag ✅
  4. Switch-bundler enhancements: Consider adding a --dry-run flag for preview

🎉 Summary

This is production-ready demo code with excellent documentation, comprehensive testing, and thoughtful architecture. The issues identified are minor and mostly suggestions for enhancement rather than bugs.

Recommendation: ✅ APPROVE

The code demonstrates:

  • Strong engineering practices
  • Security awareness
  • Performance optimization
  • Excellent documentation
  • Comprehensive test coverage

Priority Fixes (Optional)

  1. Verify @shakacode-tools/react-on-rails-pro-node-renderer package name
  2. Add installation hints to TanStack Router plugin error handling
  3. Consider data-testid attributes for more robust E2E tests

Great work on this comprehensive demo! 🚀


Reviewed using repository's CLAUDE.md guidelines

- Update react_on_rails from 16.2.0.rc1 to 16.2.0
- Update react_on_rails_pro from 16.2.0.rc.1 to 16.2.0
- Update react-on-rails-pro-node-renderer npm package to 16.2.0
- Add react_on_rails_pro_license.key to .gitignore

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@claude
Copy link

claude bot commented Jan 15, 2026

Pull Request Review - TanStack Router Demo

Thank you for this comprehensive TanStack Router demo! This is a well-structured addition to the repository that demonstrates SSR with modern routing. Here's my detailed review:


Strengths

1. Excellent Documentation

  • README.md is comprehensive with clear setup instructions, limitations, and deployment guidance
  • Well-documented code with helpful comments explaining SSR constraints (especially the synchronous rendering limitation in TanStackApp.server.tsx)
  • Clear warnings about React on Rails Pro requirement throughout

2. Well-Designed Architecture

  • Clean separation between client and server rendering components
  • Proper webpack configuration for SSR with target: 'node'
  • Correct use of libraryTarget: 'commonjs2' for Node.js compatibility
  • CSS modules configured correctly with exportOnlyLocals: true for SSR

3. Comprehensive Testing

  • Excellent E2E test coverage with Playwright
  • Tests verify both SSR and client-side hydration
  • Hydration mismatch detection is well implemented
  • Tests cover URL params, search params, and nested routes

4. Best Practices

  • Follows repository conventions from CLAUDE.md
  • Proper gitignore configuration
  • Environment-specific configuration files
  • Accessibility features (aria-current on active links)

🔍 Issues & Suggestions

Critical Issues

1. React on Rails Pro Configuration Not Committed

Location: config/initializers/react_on_rails.rb:16

The configuration file mentions requiring React on Rails Pro's Node renderer, but the actual configuration is commented out:

# To configure React on Rails Pro Node renderer:
#   config.server_render_method = "NodeJS"

Issue: Users following the setup won't have SSR working without uncommenting this.

Recommendation: Either:

  • Uncomment the line with a clear comment that it requires React on Rails Pro
  • Add a check that raises a helpful error if Pro is not configured
  • Update the README to be more explicit about this step

2. Void Expression in Server Component

Location: app/javascript/src/TanStackApp/ror_components/TanStackApp.server.tsx:31

void router.load();

Issue: While the comment explains this is intentional, using void to discard a Promise without awaiting is a code smell. The comment says "async loaders are NOT supported" but the code structure is confusing.

Recommendation: Either:

  • Make it more explicit: router.load(); // Note: Promise intentionally not awaited - async loaders not supported
  • Or better, document why TanStack Router requires calling this even though we're not awaiting
  • Consider adding a TypeScript ESLint rule exception here with justification

Security Concerns

3. Default Password in Node Renderer

Location: react-on-rails-pro/node-renderer.js:29

password: process.env.RENDERER_PASSWORD || 'tanstack-demo-renderer',

Issue: Hardcoded default password in a production-style configuration file.

Recommendation:

  • Remove the default password or use a randomly generated one
  • Throw an error if RENDERER_PASSWORD is not set in production
  • Add a warning in development mode if using the default password
  • Document this requirement more prominently in the README

Performance Considerations

4. No Chunk Splitting Strategy Documented

Location: General webpack configuration

Observation: The server bundle correctly limits chunks to 1, but there's no documentation about client-side code splitting strategies.

Recommendation:

  • Document whether route-based code splitting is supported with TanStack Router
  • Add examples or guidance on implementing lazy loading if supported
  • Consider adding a note about bundle size optimization strategies

Code Quality Issues

5. Missing Error Boundary for Hydration Errors

Location: app/javascript/src/TanStackApp/ror_components/TanStackApp.client.tsx

Issue: Client component doesn't have specific error handling for hydration mismatches.

Recommendation:

  • Add an error boundary that catches and reports hydration errors
  • Consider logging these to help developers debug SSR/client mismatches
  • Example:
class HydrationErrorBoundary extends React.Component {
  componentDidCatch(error: Error) {
    if (error.message.includes('hydration')) {
      console.error('Hydration mismatch detected:', error);
      // Could send to error tracking service
    }
  }
}

6. Routes Configuration Duplication

Location: config/routes.rb and TanStack route files

Issue: Routes are defined in both Rails and TanStack Router with no validation that they match.

Recommendation:

  • Add a rake task or test that validates Rails routes match TanStack routes
  • Document this synchronization requirement more prominently
  • Consider a code generator or linter that ensures they stay in sync

7. TypeScript Strictness

Location: Multiple TypeScript files

Observation: Using any types in some places and as const assertions.

Recommendation:

  • Review if stricter TypeScript settings could be enabled
  • Ensure all props interfaces are properly typed
  • Consider adding strict: true to tsconfig if not already set

Testing Gaps

8. No Tests for Error States

Location: e2e/tanstack_router.spec.ts

Missing Coverage:

  • Error boundary rendering
  • 404 not found page
  • Navigation failures
  • Invalid URL parameters

Recommendation:

test('renders 404 page for unknown routes', async ({ page }) => {
  await page.goto('/nonexistent-route');
  await expect(page.locator('h1')).toContainText('Page Not Found');
});

test('error boundary catches route errors', async ({ page }) => {
  // Navigate to route that throws error
  // Verify error boundary displays
});

9. No SSR Performance Tests

Location: Testing suite

Missing: Tests to ensure SSR performance doesn't degrade.

Recommendation:

  • Add tests that measure SSR response time
  • Add assertions on bundle size
  • Consider lighthouse CI integration for performance metrics

Documentation Issues

10. Missing Migration Guide

Location: README.md

Issue: No guidance for users migrating from other routing solutions (React Router, etc.).

Recommendation:

  • Add a section comparing TanStack Router to React Router
  • Document migration steps from a React Router setup
  • Explain trade-offs and when to use each solution

11. Incomplete Setup Instructions

Location: README.md

Missing Steps:

  • How to obtain React on Rails Pro license
  • Node.js version requirements
  • How to configure the Node renderer password
  • Production deployment checklist

Recommendation:
Add a detailed setup section:

## Prerequisites
- Node.js 18+ (verified with `node --version`)
- Ruby 3.x (specified in .tool-versions)
- PostgreSQL running locally
- React on Rails Pro license (see docs/react-on-rails-pro-setup.md)

Minor Issues

12. Console Warnings in Development

Location: Server webpack config

The warning about private_output_path may appear even when configured correctly if checked before Shakapacker loads config.

Recommendation:

  • Verify the check happens after config is fully loaded
  • Make the warning conditional on environment (skip in test)

13. Unused Dependencies

Location: package.json (not shown in diff, would need to check)

Recommendation:

  • Run npx depcheck to identify unused dependencies
  • Remove any packages that aren't actually used

14. Hardcoded Port Numbers

Location: Multiple Procfile.dev files

rails: bundle exec rails s -p 3000

Recommendation:

  • Use environment variables for ports
  • Document port requirements in README
  • Add port conflict detection to bin/dev

📊 Test Coverage Assessment

Well Covered:

  • ✅ SSR verification for all route types
  • ✅ Client-side navigation
  • ✅ Hydration consistency
  • ✅ URL and search parameter handling

Needs Coverage:

  • ❌ Error states and boundaries
  • ❌ Network failures
  • ❌ Invalid data scenarios
  • ❌ Browser back/forward navigation
  • ❌ Direct link access vs navigation

🔒 Security Review

Concerns Identified:

  1. Default password in node-renderer.js (Critical)
  2. No rate limiting on SSR endpoint (if exposed)
  3. No input validation on URL parameters in routes

Recommendations:

  • Add validation for userId parameter (ensure numeric, bounded)
  • Sanitize search query parameters before rendering
  • Document security best practices for SSR endpoints
  • Add Content Security Policy headers in production config

🎯 Performance Review

Good:

  • Rspack for fast builds ✅
  • SWC transpiler ✅
  • Minimal server bundle ✅

Could Improve:

  • Add bundle size tracking in CI
  • Document initial page load metrics
  • Add performance budgets
  • Consider adding loading.tsx files for route-based suspense

📝 Summary & Recommendations

Must Fix Before Merge:

  1. ❗ Configure React on Rails Pro in initializer (or add runtime check)
  2. ❗ Remove or secure default password in node-renderer.js
  3. ❗ Add validation for user input in routes

Should Fix Before Merge:

  1. Add error boundary tests
  2. Document React on Rails Pro setup more clearly
  3. Add route synchronization validation
  4. Improve void expression pattern in server component

Nice to Have:

  1. Migration guide from other routers
  2. Performance tests and budgets
  3. Bundle size tracking
  4. Stricter TypeScript configuration

Conclusion

This is a solid, well-documented demo that showcases TanStack Router integration with React on Rails SSR. The architecture is sound, the testing is comprehensive, and the documentation is helpful.

The main concerns are:

  • Configuration completeness (React on Rails Pro setup)
  • Security (default password)
  • Code clarity (void expression usage)

With the critical issues addressed, this will be an excellent addition to the demo repository!

Overall Rating: 8/10 - Great work! Address the critical issues and this is merge-ready.


Review generated using repository guidelines from CLAUDE.md

TanStack Router requires Node.js APIs (URL, setTimeout, etc.) that are
not available in the default ExecJS environment. This update:

- Switch to react-on-rails-pro npm package (required when using Pro gem)
- Fix config typo: renderer_use_fallback_exec_js (underscore)
- Update node-renderer.js to use regular npm package name
- Add node-polyfills.ts for URL/URLSearchParams globals
- Import polyfills first in server-bundle.js

Note: SSR with TanStack Router still needs further investigation for
the VM sandbox polyfill approach.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@claude
Copy link

claude bot commented Jan 15, 2026

PR Review: TanStack Router Demo with SSR and TypeScript

Summary

This PR adds a comprehensive demo showcasing TanStack Router integration with React on Rails, Rspack, and SSR. The implementation is well-structured and demonstrates important concepts for building modern React applications with Rails. Overall, this is high-quality work that will serve as an excellent reference for users.

✅ Strengths

1. Excellent Documentation

  • Clear README: The README.md is comprehensive with well-organized sections covering setup, configuration, limitations, and deployment
  • Inline comments: Code includes helpful explanations, especially around SSR limitations and Node renderer requirements
  • Warning callouts: Proactive warnings about React on Rails Pro requirements and async loader limitations

2. Proper SSR Architecture

  • Correct router setup: Memory history for SSR, browser history for client (app/javascript/src/router.ts:14-19)
  • Clean separation: Server and client components properly separated (TanStackApp.server.tsx vs TanStackApp.client.tsx)
  • Node polyfills: Appropriate use of URL polyfills for SSR environment (app/javascript/src/node-polyfills.ts)

3. Comprehensive E2E Testing

  • Good coverage: Tests verify SSR, client hydration, URL params, search params, and nested routes
  • Hydration verification: Specifically tests for hydration mismatches (e2e/tanstack_router.spec.ts:123-151)
  • Practical selectors: Uses semantic selectors that are less brittle

4. Configuration Best Practices

  • Environment-specific configs: Proper webpack/rspack configuration for client vs server bundles
  • Security: enforce_private_server_bundles = true prevents server code exposure
  • Git hygiene: Appropriate .gitignore for generated files (ssr-generated/, routeTree.gen.ts, etc.)

🔍 Issues & Recommendations

Critical Issues

1. Void Operator Misuse (config/webpack/serverWebpackConfig.js:31)

void router.load();

Issue: Using void to discard a Promise is a code smell. The comment says "synchronous loaders" but router.load() returns a Promise that's being ignored. This could hide errors.

Recommendation: Either:

  • await router.load() if the component becomes async
  • Add an error handler: router.load().catch(console.error)
  • Add a comment explaining why it's safe to ignore: // Synchronously loads route tree; no async loaders configured

2. TypeScript any Usage (app/javascript/src/node-polyfills.ts:14, 18)

(globalThis as any).URL = URL;
(globalThis as any).URLSearchParams = URLSearchParams;

Issue: Using any bypasses type safety.

Recommendation:

declare global {
  var URL: typeof import('url').URL;
  var URLSearchParams: typeof import('url').URLSearchParams;
}

if (typeof globalThis.URL === 'undefined') {
  globalThis.URL = URL;
}

if (typeof globalThis.URLSearchParams === 'undefined') {
  globalThis.URLSearchParams = URLSearchParams;
}

Performance Considerations

3. Router Instance Creation (TanStackApp.server.tsx:25)

Observation: A new router instance is created on every SSR render.

Recommendation: This is acceptable for the demo, but document that production apps might want to consider router instance pooling for high-traffic scenarios. The current approach is simpler and avoids state leakage between requests.

4. No Code Splitting

Observation: Server bundle uses LimitChunkCountPlugin({ maxChunks: 1 }) which is correct for SSR, but the client bundle doesn't appear to use route-based code splitting.

Recommendation: For a production-focused demo, consider adding a follow-up example showing TanStack Router's lazy loading:

const Route = createRoute({
  component: lazy(() => import('./HeavyComponent'))
})

Security

5. CSRF Token Handling

Observation: No forms POST data in this demo, but if users extend it, they'll need CSRF tokens.

Recommendation: Add a note in README.md about CSRF token handling when adding forms that POST to Rails:

### Adding Forms
When adding forms that submit to Rails endpoints, include CSRF tokens:
\`\`\`tsx
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content;
\`\`\`

Code Quality

6. Route Synchronization (config/routes.rb)

Observation: Each TanStack route requires a manual Rails route definition. This is documented but error-prone.

Recommendation:

  • ✅ Already documented in README.md (lines 178-192)
  • Consider adding a rake task that validates route synchronization:
# lib/tasks/routes_check.rake
task :check_tanstack_routes => :environment do
  # Compare TanStack routes with Rails routes
end

7. Missing Error Boundaries

Observation: No React error boundaries in the route tree.

Recommendation: Add an error boundary to the root route to catch runtime errors gracefully:

// app/javascript/src/routes/__root.tsx
errorComponent: ({ error }) => (
  <div>Error: {error.message}</div>
)

Testing

8. E2E Test Robustness (e2e/tanstack_router.spec.ts:140)

Observation: Tests use waitForLoadState('domcontentloaded') which might be flaky with HMR.

Recommendation: The current approach is reasonable for a demo. For production apps, consider:

  • Using page.waitForSelector() with specific elements
  • Adding data-testid attributes for more reliable selection
  • Testing with HMR disabled in CI

Documentation

9. Node Renderer Setup

Observation: References to docs/react-on-rails-pro-setup.md but setup instructions could be clearer.

Recommendation: Add a troubleshooting section:

### Troubleshooting SSR

**Error: "setTimeout is not defined"**
- Ensure React on Rails Pro is installed
- Verify `config.server_render_method = "NodeJS"` is set
- Check that node-renderer process is running (in Procfile.dev)

**Hydration Mismatches**
- Check that Rails routes match TanStack routes
- Verify `initialUrl` is passed correctly from controller

🎯 Minor Observations

  1. Consistent Naming: tanstack_app (lowercase with underscore) vs TanStackApp (PascalCase) - this is intentional Rails convention, good ✅

  2. Babel Config: Still using babel.config.js even though Rspack uses SWC by default. This might be for fallback compatibility - consider documenting why it's kept.

  3. TypeScript Strict Mode: No tsconfig.json visible in the diff - consider adding strict TypeScript configuration for better type safety.

  4. Package.json Scripts: Clean and minimal, appropriate for a demo ✅

  5. Procfile Variants: Good to see multiple Procfile options for different dev scenarios ✅

📊 Test Coverage Assessment

Current Coverage: ✅ Excellent

  • SSR verification ✅
  • Client hydration ✅
  • URL routing ✅
  • Search params ✅
  • Nested routes ✅
  • Hydration mismatch detection ✅

Missing Coverage (nice to have):

  • 404 handling
  • Error boundary triggers
  • Link preloading behavior
  • History back/forward navigation

🔒 Security Assessment

No major security issues found

  • Server bundle isolation: ✅ Properly configured
  • CSRF: N/A for this demo (no forms)
  • XSS: Using React which escapes by default ✅
  • Dependencies: Would need separate audit, but versions look current

🚀 Performance Assessment

Good baseline performance

  • Rspack for fast builds ✅
  • SSR for initial render performance ✅
  • Appropriate caching strategies could be documented

Final Recommendation

✅ APPROVE with minor suggested improvements

This is excellent work that demonstrates advanced React on Rails patterns. The code quality is high, documentation is thorough, and the demo achieves its educational goals.

Suggested Action Items (Optional)

  1. Fix the any type casts in node-polyfills.ts (5 min)
  2. Add error boundary to root route (10 min)
  3. Add troubleshooting section to README (10 min)
  4. Consider adding route synchronization validation task (30 min, optional)

None of these are blockers for merging. Great work! 🎉


Reviewed with Claude Code

justin808 added a commit to shakacode/react_on_rails that referenced this pull request Mar 4, 2026
Add createTanStackRouterRenderFunction() to the react-on-rails npm
package, providing a render function factory for TanStack Router apps
with server-side rendering support.

This encapsulates the private API workarounds needed for synchronous
SSR (router.__store.setState() and router.ssr flag) inside
ShakaCode-maintained code, so consumer apps only use public APIs.

Server-side: synchronously injects route matches and renders with
renderToString. Client-side: hydrates with browser history and
dehydrated router state from the server.

Also includes an async path (serverRenderTanStackAppAsync) for use
with React on Rails Pro NodeRenderer when rendering_returns_promises
is enabled.

Related: #2298, #2299
Ref: printivity/printivity#2571, shakacode/react_on_rails-demos#104

Co-Authored-By: Claude Opus 4.6 <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