Almost every React app needs routing, and almost every React team picks React Router by default — the same library the ecosystem has used for a decade. But 2026 is not 2016. TanStack Router has brought fully type-safe routing to the mainstream with 15,000+ stars, and Wouter proves that a complete router can fit in 2.2 KB. If you are starting a new project today, the default choice may no longer be the right one. Here is the comparison that will save you a rewrite.
TL;DR / Quick Verdict
- Pick React Router v7 if you want the safest default, a massive ecosystem of tutorials and middleware, and first-class support for loaders, actions, and SSR in one package.
- Pick TanStack Router if type safety is a hard requirement for your team — every route path, search parameter, and query key is checked at compile time.
- Pick Wouter if you are shipping an embeddable widget, an extension popup, or any bundle-size-sensitive app, and your routing needs are simple.
The Contenders
All three are open source, MIT-licensed, and actively maintained as of August 2026. They differ radically in philosophy: React Router is the battle-tested generalist, TanStack Router is the type-safety maximalist, and Wouter is the minimalist that refuses to grow.
| Library | GitHub Stars | Last Updated | Bundle Size (gzip) | Type-Safe Routes | Data Loaders | SSR / Framework Mode | File-Based Routing |
|---|---|---|---|---|---|---|---|
| React Router | 56,553 | 2026-08 | ~14 KB | Partial (via codegen) | Yes (loaders/actions) | Yes (v7 framework mode) | Via Vite plugin |
| TanStack Router | 14,931 | 2026-08 | ~15 KB | Full (paths, params, search) | Yes (loader/search) | Yes | Yes (built-in) |
| Wouter | 7,865 | 2026-08 | ~2.2 KB | Basic (typed params) | No | No | No |
React Router v7 (released in late 2024 and now the current major line) unified the old react-router-dom and react-router packages into a single react-router dependency and added optional “framework mode” with loaders, actions, and SSR. TanStack Router, from the same author as TanStack Query and TanStack Table, was built from the ground up around end-to-end type inference. Wouter is a single-file router by Andrew Safin that reimplements the classic useRoute/useLocation hook API with zero dependencies.
Use Case → Recommended Tool
| Use Case | Recommended | Why |
|---|---|---|
| Large production SPA with a team of 5+ developers | TanStack Router | Compile-time route checking eliminates entire classes of broken-link bugs |
| Legacy codebase migration or the “boring choice” | React Router | The entire ecosystem’s tutorials, examples, and job candidates assume it |
| Next.js-style full-stack app without Next.js | React Router v7 | Framework mode gives you loaders, actions, and SSR on your own Vite setup |
| Embedded widget, iframe, or browser extension | Wouter | 2.2 KB gzipped; hash-based mode works without server config |
| Strictly typed search parameters (filters, pagination) | TanStack Router | search is validated and typed per route — no more `string |
| Prototype or weekend project | Wouter | One dependency, one file of docs, zero config |
React Router — The Battle-Tested Default
With 56,553 stars (updated August 2026), React Router is the most-used routing library in the React ecosystem by a wide margin. Version 7 modernized the API: you now install a single react-router package, define routes as a data structure, and let RouterProvider drive the tree. Loaders and actions give you framework-grade data handling without committing to a meta-framework.
| |
The trade-off is that type safety is not automatic. Route paths are still plain strings — the library can infer params only when you opt into its codegen plugin or hand-write typed route objects. That is fine for most teams, but it is exactly the gap TanStack Router exploits.
| |
TanStack Router — Type Safety as the Default
TanStack Router (14,931 stars, updated August 2026 — one of the most active routers in the ecosystem right now) treats the type system as a first-class citizen. Every Link target, every route param, and every search parameter is inferred from your route tree. Invalid routes fail the build, not production.
| |
File-based routing is built in: name a file users.$userId.tsx inside your routes directory and the route tree generates itself, with the same type inference. For teams burned by broken deep links or untyped query strings, this is the strongest argument in the category. The cost: a steeper learning curve and a router that is more opinionated about how you structure your app.
| |
Wouter — The 2.2 KB Minimalist
Wouter (7,865 stars, updated August 2026) proves that a router does not need to be big. The entire library is ~2.2 KB gzipped with zero dependencies, and it exposes a tiny hook-based API that feels like the platform: useLocation for the current path, useRoute for matching, and a <Route> component for declarative rendering. It supports both history and hash modes — the latter being perfect for static hosting and file:// contexts.
| |
The trade-offs are real: no loaders, no SSR story, and no codegen — params are typed but route matching is runtime string matching. For an embeddable widget or a docs site, that is a feature, not a bug. You can also swap in wouter’s memory and static location adapters for tests and prerendering.
| |
Bundle Size and Performance Reality Check
Bundle size matters differently depending on where the router runs:
- Wouter at ~2.2 KB gzipped is invisible in any bundle — it will never be the reason your page is slow.
- React Router and TanStack Router both land in the 10–20 KB gzipped range for core routing. TanStack Router’s codegen adds a few KB but buys compile-time checking.
- Runtime performance is a non-issue in all three for realistic apps: routing is a few string comparisons and a render. The real performance wins come from code splitting (lazy routes), which all three support. React Router and TanStack Router both offer
lazyroute definitions; with Wouter you useReact.lazyyourself.
The honest conclusion: if you are choosing on bundle size alone, only Wouter’s size is meaningfully different. Everything else is DX and safety.
Common Pitfalls and Migration Traps
- Treating “type-safe” as binary. React Router’s codegen gives partial typing; TanStack Router’s is total. If your team expects full inference, verify the actual DX before committing — a “partial” experience can be more confusing than none.
- Search params as strings. With React Router,
useSearchParams()returns string values, so?page=2needs manualNumber()parsing and defaulting. TanStack Router’svalidateSearchdoes this once, centrally, with types — worth stealing the pattern even if you stay on React Router. - Hash routing is a trap for deep links. Wouter’s hash mode is great for widgets but breaks server-side link previews and analytics referrers. Use history mode on real websites.
- Mixed history state with multiple routers. Mounting two router instances (e.g., an embedded React app inside a host app) causes history fights. Wouter’s memory location is the clean escape hatch; React Router and TanStack Router both support memory history for sub-apps too.
- Upgrading from React Router v6. The v7 migration is mostly mechanical (
react-router-dom→react-router), butuseRoutesandRoutes-style declarative APIs still exist while the data APIs are the recommended path. Don’t mix both styles in one codebase. - TanStack Router’s opinionated tree. The generated route tree becomes part of your repo; codegen caches and stale trees cause confusing errors. Commit the generated files and regenerate on every route change in CI.
- SSR watermarks. All three render on the server, but hydration mismatches around
useLocation(e.g., host header differences) are the classic bug. Normalize the URL before rendering on the server.
For more frontend ecosystem comparisons, see our JavaScript state management roundup, the TanStack Query vs SWR vs RTK Query data-fetching comparison, and the JavaScript data grid shootout.
FAQ
Is TanStack Router a drop-in replacement for React Router?
No. The APIs are intentionally different (route trees vs. route objects, typed Link props vs. string to). You can migrate incrementally route by route, but you will rewrite link components and navigation calls along the way.
Does React Router v7 still work with React 18?
Yes. React Router v7 supports React 18 and 19, and it works with plain Vite setups, Remix-compatible deployments, and custom servers via the react-router package’s framework mode.
Can Wouter handle nested routes?
Wouter matches flat paths; for nesting you compose useRoute matches manually or use Switch ordering. If you need deeply nested layouts with outlet rendering, React Router or TanStack Router is the better fit.
Which router has the best developer tooling? TanStack Router ships a dedicated devtools panel (route tree inspection, cache state) similar to TanStack Query’s. React Router relies on browser devtools and its error boundary outputs. Wouter intentionally ships none.
Is file-based routing worth switching for? If your team already uses file-based conventions (like Next.js), TanStack Router’s built-in file routing feels natural and removes route-config drift. If you prefer explicit config, React Router’s data objects are clearer to review.
Do these routers work with React Native?
React Router has a dedicated react-router-native package. TanStack Router and Wouter are web-first; community forks exist but are not official.
💰 想测试你的市场判断力?我用 Polymarket 做预测市场交易——这是全球最大的预测市场平台,从大选结果到技术监管时间线,什么都可以押注。和赌博不同,这是真正的信息市场:你懂的信息越多,胜率越高。我靠预测技术相关事件的走向已经赚了不少。用我的邀请链接注册:Polymarket.com