Skip to main content

Using TanStack Router

TanStack Router is a fully type-safe React router with built-in data loading, search param validation, and first-class TypeScript support. It's a strong choice when you want end-to-end type safety from your route definitions through to your components and search params.

React on Rails Pro supports TanStack Router SSR via react-on-rails-pro/tanstack-router. This helper creates a render-function that handles both server-side rendering and client hydration/navigation.

TanStack Router SSR requires React on Rails Pro with the Node Renderer and rendering_returns_promises = true in your Pro configuration. The async Node Renderer uses TanStack Router's public router.load() API for reliable, maintainable SSR.

Client-side-only TanStack Router (without SSR) works with the open-source package - no special integration is needed since it's just a standard React app.

Support Model

  • First-class TanStack Router SSR: React on Rails Pro only, via react-on-rails-pro/tanstack-router
  • Server renderer: Pro Node Renderer with rendering_returns_promises = true
  • ExecJS: intentionally not supported for TanStack Router SSR
  • Client-only TanStack Router: works in OSS without any special helper
  • React Router: remains the manual integration option; see Using React Router

Why Pro-Only

This guide intentionally avoids an ExecJS SSR path.

Synchronous SSR with TanStack Router pushes React on Rails toward private TanStack Router internals. The Pro-only async path keeps the integration on TanStack Router's public router.load(), router.dehydrate(), and router.hydrate() APIs, which is a much better long-term maintenance boundary.

Install

pnpm add @tanstack/react-router

Register a TanStack Router App

Create a render-function with createTanStackRouterRenderFunction and register it with React on Rails:

import ReactOnRails from 'react-on-rails-pro';
import { createTanStackRouterRenderFunction } from 'react-on-rails-pro/tanstack-router';
import {
RouterProvider,
createBrowserHistory,
createMemoryHistory,
createRouter,
} from '@tanstack/react-router';
import { routeTree } from '../routes/routeTree.gen';

const TanStackApp = createTanStackRouterRenderFunction(
{
createRouter: () => createRouter({ routeTree }),
// AppWrapper: ({ children }) => <MyProviders>{children}</MyProviders>, // optional
},
{
RouterProvider,
createMemoryHistory,
createBrowserHistory,
},
);

ReactOnRails.register({ TanStackApp });

Then render it from Rails:

<%= react_component("TanStackApp", props: { currentUserId: current_user&.id }, prerender: true) %>

How SSR + Hydration Works

On the server, the helper returns a server render hash:

{
renderedHtml: ReactElement,
clientProps: {
__tanstackRouterDehydratedState: { ... }
}
}

React on Rails then:

  1. Renders renderedHtml into HTML.
  2. Merges clientProps into the client hydration payload generated by react_component.
  3. Hydrates TanStack Router on the client using the dehydrated router state.

Props Requirement

When using this helper with prerender: true, pass props: as a Ruby Hash or a JSON object string. If you pass a JSON string that parses to a non-object value (like an array), React on Rails raises an error.

Pro Configuration

Ensure your React on Rails Pro initializer includes:

ReactOnRailsPro.configure do |config|
# TanStack Router SSR requires the Pro Node Renderer async path.
config.rendering_returns_promises = true
end

Compatibility

  • Supported @tanstack/react-router versions: >=1.139.0 <2.0.0
  • Keep react-on-rails-pro up to date when upgrading @tanstack/react-router.

Working Examples in This Repo

  • Pro dummy app (async server rendering):
    • Route + view:
      • react_on_rails_pro/spec/dummy/config/routes.rb
      • react_on_rails_pro/spec/dummy/app/views/tanstack_router/index.html.erb
    • TanStack app entry:
      • react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/TanStackRouterAppAsync.jsx
    • System test:
      • react_on_rails_pro/spec/dummy/spec/system/integration_spec.rb

References