|
| 1 | +--- |
| 2 | +sidebar_label: 'RoR Pro vs TanStack Start' |
| 3 | +description: 'How React on Rails Pro and TanStack Start divide the full stack: which TanStack libraries complement Rails, where server logic and data live, RSC vs. server functions, type safety, and a capability comparison.' |
| 4 | +--- |
| 5 | + |
| 6 | +# React on Rails Pro and TanStack Start: Two Ways to Own the Full Stack |
| 7 | + |
| 8 | +> [!NOTE] |
| 9 | +> The TanStack client libraries (Query, Router, Table) work with open-source React on Rails. React |
| 10 | +> Server Components and TanStack Router **SSR** require [React on Rails Pro](../react-on-rails-pro.md) |
| 11 | +> with the Node renderer. |
| 12 | +
|
| 13 | +> [!NOTE] |
| 14 | +> **Summary for AI agents:** Use this page to understand how React on Rails Pro and TanStack Start |
| 15 | +> divide responsibility for the full stack — and why most of the "TanStack vs. React on Rails" |
| 16 | +> confusion dissolves once you separate the TanStack _libraries_ (Query, Router, Table) from the |
| 17 | +> TanStack _framework_ (Start). It is an _architectural_ explainer, not a how-to. For building with the |
| 18 | +> libraries on Rails, route to [TanStack Router](../../oss/building-features/tanstack-router.md). For the |
| 19 | +> RSC contract this page references, route to [RoR Pro vs. Next.js RSC](./nextjs-comparison.md). For |
| 20 | +> "which should I pick," route to the |
| 21 | +> [Decision Guide](../../oss/getting-started/comparing-react-on-rails-to-alternatives.md). |
| 22 | +
|
| 23 | +> [!NOTE] |
| 24 | +> **Accuracy note.** React on Rails Pro details are verified against this repository. TanStack Start |
| 25 | +> details are described at the **conceptual** level and reflect TanStack Start as of **2026** (it reached |
| 26 | +> a stable 1.x release earlier in the year). The TanStack ecosystem evolves quickly; treat specific |
| 27 | +> TanStack names and feature labels here as illustrative of an idea, not as a stable API. |
| 28 | +
|
| 29 | +"Should we use TanStack or React on Rails?" is the wrong question, because **"TanStack" is not one |
| 30 | +thing.** TanStack publishes a suite of client libraries _and_ a full-stack framework, and they sit on |
| 31 | +opposite sides of the line that matters to a Rails team. Once you split them, the comparison is clear: |
| 32 | +the libraries are complementary, and only the framework is a genuine either/or. |
| 33 | + |
| 34 | +## "TanStack" is two different things |
| 35 | + |
| 36 | +| What | What it is | Role for a Rails team | |
| 37 | +| ------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | |
| 38 | +| **TanStack Query** | Client-side server-state cache (fetch, cache, invalidate) | **Complement.** Point it at a Rails JSON API. | |
| 39 | +| **TanStack Router** | Type-safe client router with data loading + search-param validation | **Complement.** Client-only works in OSS; SSR is a Pro feature (see [TanStack Router](../../oss/building-features/tanstack-router.md)). | |
| 40 | +| **TanStack Table** | Headless table/data-grid primitives | **Complement.** Renders from Rails-served data. | |
| 41 | +| **TanStack Start** | Full-stack React framework (TanStack Router + Vite + Nitro + server functions) | **Substitute.** This is the layer that overlaps with Rails. | |
| 42 | + |
| 43 | +Adopting Query, Router, and Table does **not** require leaving Rails — React on Rails apps use them on |
| 44 | +top of a Rails backend today. The only part you are choosing _between_ is the framework: **TanStack |
| 45 | +Start or Rails as the thing behind your React app.** The rest of this page is about that one choice. |
| 46 | + |
| 47 | +## Two ways to get a server |
| 48 | + |
| 49 | +A mental model before the mechanics. Every non-trivial React app needs a server for the same jobs: |
| 50 | +data access, authorization, persistence, background work, and rendering HTML for first paint and SEO. |
| 51 | +The two stacks supply that server very differently. |
| 52 | + |
| 53 | +- **TanStack Start brings the _wiring_ for a server, and you supply the server.** Start is |
| 54 | + **SSR-first**: routes are server-rendered by default, with fine-grained selective SSR to opt a route |
| 55 | + out (`ssr: false` / `'data-only'`, or SPA mode). Its server story is **server functions** — typed functions guaranteed to run server-side, |
| 56 | + callable from the client as if they were local. But Start ships **no database, ORM, or auth of its |
| 57 | + own**; it works with "any database, bring your own stack" (Drizzle is the common choice). You assemble |
| 58 | + the backend; Start types the boundary to it. |
| 59 | +- **React on Rails Pro brings the React station, and Rails is already the server.** Your Rails app has |
| 60 | + the kitchen — ActiveRecord, authorization, jobs, caching, mature libraries. React on Rails (and React |
| 61 | + on Rails Pro for SSR/RSC) adds React as the view layer on top, with the TanStack client libraries |
| 62 | + where they help. You keep Rails and add React; you do not assemble a backend. |
| 63 | + |
| 64 | +Neither is "better." They answer different questions. _"I'm building a React app and have no backend |
| 65 | +yet"_ points toward TanStack Start. _"I have, or want, Rails, and want a modern React frontend on it"_ |
| 66 | +points toward React on Rails. |
| 67 | + |
| 68 | +## Where your server logic lives |
| 69 | + |
| 70 | +The strongest line in the TanStack Start pitch is **colocation**: server code sits next to the |
| 71 | +component that needs it, with no separate API endpoint and no serializer. A server function reads from |
| 72 | +your database and returns typed data to the component. |
| 73 | + |
| 74 | +React on Rails Pro's answer to that colocation pitch is **React Server Components**. An RSC runs on the |
| 75 | +server and renders from data your Rails controller prepared — passed as props, or streamed as |
| 76 | +[async props](../../oss/migrating/rsc-data-fetching.md) for slow data — with no `/api` round-trip and |
| 77 | +no serializer for that view. You get the colocation benefit; the difference is _what_ the server code |
| 78 | +is: a Rails-prepared data flow rather than a TypeScript function, so authorization, caching, and the |
| 79 | +database stay in Rails where they already live. (RSC is a Pro feature requiring the |
| 80 | +[node renderer](../node-renderer.md); for how the RSC contract itself works, see |
| 81 | +[RoR Pro vs. Next.js RSC](./nextjs-comparison.md).) |
| 82 | + |
| 83 | +For the interactive pieces that **mutate**, both stacks still cross a network boundary: Start calls a |
| 84 | +typed server function; React on Rails posts to a Rails controller (often with TanStack Query driving the |
| 85 | +request). Start's server-function shorthand is genuinely less boilerplate for that path today; the trade |
| 86 | +is that the function — and the business logic inside it — lives in your UI's runtime rather than in |
| 87 | +Rails. |
| 88 | + |
| 89 | +## The backend you assemble vs. the backend you have |
| 90 | + |
| 91 | +This is the difference that usually decides it for an existing Rails team. TanStack Start is, by design, |
| 92 | +a frontend framework with a server-function transport — **it does not provide the backend**. |
| 93 | +Persistence, schema and migrations, an ORM, authentication, authorization, and background jobs are all |
| 94 | +things you add from separate libraries and own yourself. |
| 95 | + |
| 96 | +Rails _is_ that backend, batteries included. So the choice is rarely "Start vs. Rails" as peers; it is |
| 97 | +"a backend you assemble out of Drizzle-plus-libraries behind Start's server functions" vs. "the Rails |
| 98 | +backend you already have, with React in front." If you have no Rails investment and want one language |
| 99 | +end to end, assembling is reasonable. If you have Rails — or would choose it for the data layer — Start |
| 100 | +is solving a problem you have already solved, in a second language. |
| 101 | + |
| 102 | +## Rendering and first paint |
| 103 | + |
| 104 | +- **TanStack Start** is **SSR-first**: routes are server-rendered by default. It offers fine-grained |
| 105 | + **selective SSR** — opt a route out with `ssr: false` or `ssr: 'data-only'`, or use SPA mode — which |
| 106 | + is a genuine strength for tuning per-route rendering. |
| 107 | +- **React on Rails** server-renders React from Rails — in open-source via ExecJS, or via the Node |
| 108 | + renderer in **React on Rails Pro**, which adds streaming SSR and RSC: the HTML shell streams |
| 109 | + immediately and server-rendered data streams in progressively. TanStack Router state can be SSR'd and hydrated via Pro |
| 110 | + ([TanStack Router](../../oss/building-features/tanstack-router.md)), and a TanStack Query cache can be |
| 111 | + seeded from Rails so the first page of data is in the initial HTML rather than fetched after |
| 112 | + hydration. |
| 113 | + |
| 114 | +## Type safety across the boundary |
| 115 | + |
| 116 | +Type safety is a real Start advantage worth stating plainly. Because Start's server functions are |
| 117 | +TypeScript on both sides, the client/server boundary is **end-to-end typed** with no codegen. |
| 118 | + |
| 119 | +React on Rails talks to Rails over a JSON boundary that is **not typed by default** — a Ruby backend and |
| 120 | +a TypeScript client do not share a type system. Teams close this by generating TypeScript types from the |
| 121 | +Rails side (serializers or schema). That is the honest gap: Start gets cross-boundary types for free; on |
| 122 | +Rails you generate them. (Improving this out of the box is on the roadmap — check the |
| 123 | +[release notes](../release-notes/index.md) for the current state.) |
| 124 | + |
| 125 | +## Comparing capabilities |
| 126 | + |
| 127 | +> Marked "as of 2026" where a row reflects a current state rather than a permanent design choice. Both |
| 128 | +> ecosystems move quickly — check each project's release notes before treating a label as permanent. |
| 129 | +
|
| 130 | +| Capability | React on Rails (+ Pro) | TanStack Start | |
| 131 | +| --------------------------------- | --------------------------------------------------- | --------------------------------------- | |
| 132 | +| Client data cache (Query) | Yes — TanStack Query against Rails | Yes — TanStack Query | |
| 133 | +| Type-safe client routing (Router) | Yes — TanStack Router; SSR is Pro | Yes — built in (Router is the core) | |
| 134 | +| Headless tables (Table) | Yes — TanStack Table | Yes — TanStack Table | |
| 135 | +| Backend (DB, ORM, auth, jobs) | Rails, batteries included | Bring your own (e.g. Drizzle) | |
| 136 | +| Server-logic colocation | RSC (Pro) + Rails controllers | Server functions | |
| 137 | +| Typed client/server boundary | Generate types from Rails (as of 2026) | Built-in (TypeScript on both sides) | |
| 138 | +| Default rendering | Server-rendered (Rails); streaming SSR + RSC in Pro | SSR by default; selective per-route SSR | |
| 139 | +| Language(s) | Ruby + TypeScript | TypeScript end to end | |
| 140 | +| Hosting model | Your Rails app + a Node renderer process (Pro) | One Node (or Edge) server process | |
| 141 | + |
| 142 | +The pattern mirrors the framework-vs.-libraries split: **the TanStack client libraries are shared |
| 143 | +ground** — both stacks use Query, Router, and Table. The divergence is entirely in the **server tier**: |
| 144 | +Start gives you a typed transport to a backend you assemble in one language; React on Rails gives you |
| 145 | +Rails as the backend with React (and RSC) in front, at the cost of two languages and, for Pro SSR/RSC, a |
| 146 | +couple more moving parts. |
| 147 | + |
| 148 | +## Developer experience: one process vs. several |
| 149 | + |
| 150 | +TanStack Start development is a single Vite-driven command with one server process and a fast dev loop — |
| 151 | +a real strength, and part of why teams cite it when leaving heavier frameworks. |
| 152 | + |
| 153 | +React on Rails Pro development orchestrates several processes — Rails, the client dev-server (HMR), the |
| 154 | +bundle watchers, and the node renderer — typically managed together by `bin/dev` and a Procfile. That is |
| 155 | +the honest price of bolting onto Rails rather than owning the server. Choosing **Rspack** (Rust + SWC) |
| 156 | +closes much of the compile-speed gap while keeping the webpack-compatible config Shakapacker relies on. |
| 157 | + |
| 158 | +## When you should choose TanStack Start |
| 159 | + |
| 160 | +To keep this honest: if you are **greenfield with no backend**, want **one language** end to end, and |
| 161 | +are a small team **optimizing for raw velocity**, TanStack Start is a genuinely good choice and React on |
| 162 | +Rails is not the pitch. Start is a mature, well-designed framework; the case for React on Rails is |
| 163 | +specifically about teams that have, or want, **Rails as a real backend** under a modern React frontend. |
| 164 | + |
| 165 | +## Which should you choose? |
| 166 | + |
| 167 | +This page is about _architecture_, not selection. For the decision itself: |
| 168 | + |
| 169 | +- [Decision Guide: React on Rails vs. TanStack Start and other alternatives](../../oss/getting-started/comparing-react-on-rails-to-alternatives.md) |
| 170 | +- [RoR Pro vs. Next.js RSC](./nextjs-comparison.md) — the RSC contract referenced above, compared across stacks |
| 171 | +- [React on Rails Pro + TanStack starter](https://github.com/shakacode/react-on-rails-starter-tanstack) — a runnable app using TanStack Query, Router, and Table against a Rails backend, with Pro SSR/RSC |
| 172 | + |
| 173 | +## Summary |
| 174 | + |
| 175 | +"TanStack vs. React on Rails" is really two questions. The TanStack **client libraries** — Query, |
| 176 | +Router, and Table — are **complementary**: React on Rails apps use them on top of a Rails backend today. |
| 177 | +Only TanStack **Start**, the full-stack framework, is a **substitute**, and the substitution is |
| 178 | +specifically for the **server tier**. TanStack Start is SSR-first (server-rendered by default, with |
| 179 | +selective per-route SSR) and a typed **server-function** transport, but it ships **no backend** — you |
| 180 | +bring the database, ORM, auth, and jobs. React on Rails keeps **Rails** as that backend, batteries included, with React as the view layer; |
| 181 | +React on Rails Pro adds streaming SSR and **React Server Components**, which remove the extra `/api` |
| 182 | +round-trip for a view while keeping data access in Rails. Start wins on one language and a free |
| 183 | +end-to-end type boundary; React on Rails wins when you want a real backend — Rails — under your React, |
| 184 | +and would rather adopt the TanStack libraries on top of it than rebuild the backend in JavaScript. |
| 185 | + |
| 186 | +## Related documentation |
| 187 | + |
| 188 | +- [TanStack Router on React on Rails](../../oss/building-features/tanstack-router.md) — using TanStack Router, with Pro SSR support |
| 189 | +- [RoR Pro vs. Next.js RSC](./nextjs-comparison.md) — how the RSC contract works, compared across stacks |
| 190 | +- [React Server Components overview](./index.md) — the full RSC documentation set |
| 191 | +- [Decision Guide](../../oss/getting-started/comparing-react-on-rails-to-alternatives.md) — choosing between React on Rails, TanStack Start, Next.js, and other alternatives |
0 commit comments