How to Use TanStack Router: A Modern, Type-Safe Router for React
TanStack Router: A Modern, Type-Safe Router for React
For React developers, routing is a critical aspect of building applications. While React Router and Next.js Router dominate the ecosystem, a new contender TanStack Router offers a fresh approach. Designed for modern, scalable, and type-safe navigation, it incorporates best practices from solutions like Remix and Next.js.
Key Features of TanStack Router
1. Complete Type-Safety
Built with TypeScript in mind, TanStack Router ensures full type coverage, minimizing runtime errors. IDE support highlights issues like incorrect parameter names or missing query parameters.
2. Built-in Data Loaders and Caching
With API data fetching integrated directly into the router:
- Supports parallel data loading across routes.
- Stale-while-revalidate caching out of the box, or integration with TanStack Query.
- Prefetches data on hover for faster navigation.
3. Advanced Query Parameter Management
TanStack Router offers:
- JSON serialization for complex objects.
- Automatic validation of query parameters.
- Middleware for centralized management of query updates.
- Declarative and programmatic tools like
useSearch
and<Link>
.
4. SSR Compatibility and Framework Integration
While primarily a client-side router, TanStack Router supports SSR and streaming. Upcoming integrations with frameworks like TanStack Start aim to rival Next.js.
5. Code Splitting and Lazy Loading
- Critical and non-critical parts of routes can be separated.
- Supports file-based routing for automatic tree generation.
6. Developer Tools
Includes official devtools to visualize route structures, cache states, and data loaders.
Comparison with Other Solutions
TanStack Router vs. React Router
- Type Safety: TanStack Router offers full type safety, unlike React Router.
- Caching: Built-in caching and data loaders outperform React Router’s capabilities.
- Query Parameters: Comprehensive tools for query management.
TanStack Router vs. Next.js
- Lightweight: Ideal for client-side routing without the overhead of a full framework.
- Flexibility: Easier integration into existing SPAs compared to the monolithic Next.js approach.
A detailed comparison can be found here.
Basic Setup
Code-Based Routing
1 import {2 createRouter,3 createRootRoute,4 createRoute,5 RouterProvider,6 } from '@tanstack/react-router';78 // Define routes9 const rootRoute = createRootRoute();10 const aboutRoute = createRoute({ getParentRoute: () => rootRoute, path: '/about' });1112 // Create router instance13 const router = createRouter({ routeTree: rootRoute.addChildren([aboutRoute]) });1415 // Use the router16 <RouterProvider router={router} />;
File-Based Routing
Structure your routes in a /routes
directory, and use a plugin to auto-generate the route tree.
Let's add some routes
1 import { StrictMode } from 'react';2 import ReactDOM from 'react-dom/client';3 import {4 createRouter,5 createRootRoute,6 createRoute,7 RouterProvider,8 Outlet,9 Link,10 } from '@tanstack/react-router';1112 import { TanStackRouterDevtools } from '@tanstack/router-devtools';1314 // Define the root route with a shared layout15 const rootRoute = createRootRoute({16 component: () => (17 <>18 <nav>19 <Link to="/">Home</Link>20 <Link to="/about">About</Link>21 </nav>22 <Outlet />23 <TanStackRouterDevtools />24 </>25 ),26 });2728 // Define child routes29 const indexRoute = createRoute({30 getParentRoute: () => rootRoute,31 path: '/',32 component: () => <div>Home Page</div>,33 });3435 const aboutRoute = createRoute({36 getParentRoute: () => rootRoute,37 path: '/about',38 component: () => <div>About Us</div>,39 });4041 // Build the route tree42 const routeTree = rootRoute.addChildren([indexRoute, aboutRoute]);4344 // Create the router instance45 const router = createRouter({ routeTree });4647 // Register types for TypeScript compatibility48 declare module '@tanstack/react-router' {49 interface Register {50 router: typeof router;51 }52 }5354 // Entry point for the application55 ReactDOM.createRoot(document.getElementById('root')!).render(56 <StrictMode>57 <RouterProvider router={router} />58 </StrictMode>59 );
This setup creates a simple router with two routes ("/
" and "/about
").
Each route renders its corresponding component when accessed.
File-Based Routing
For a more magical approach, consider using file-based routing. The folder structure within /src/routes/...
defines the hierarchy, and a plugin auto-generates the route tree.
Example structure:
1 src/2 ┣ routes/3 ┃ ┣ __root.tsx # Root route4 ┃ ┣ index.tsx # /5 ┃ ┗ about.tsx # /about6 ┣ main.tsx7 ┗ routeTree.gen.ts (auto-generated)
In __root.tsx
, you can define a common layout, headers, footers, Devtools, and other global components.
Fetching Data with Loaders
TanStack Router provides built-in support for loaders, which fetch and cache data before rendering components.
1 // src/routes/posts.tsx2 import { createFileRoute } from '@tanstack/react-router';3 import { fetchPosts } from '@/api';45 // Define a route with a loader to fetch posts6 export const Route = createFileRoute('/posts')({7 loader: async () => fetchPosts(),8 component: Posts,9 });1011 function Posts() {12 // Access loader data13 const posts = Route.useLoaderData();1415 return (16 <ul>17 {posts?.map((post) => (18 <li key={post.id}>{post.title}</li>19 ))}20 </ul>21 );22 }
With this setup, navigating to /posts
triggers data loading and caching before rendering. If revisited during the cache's validity period, no additional fetch requests are needed.
Search Parameter Handling and Validation
Using ?searchQuery=...
is an effective way to manage UI state like filters or pagination. With TanStack Router, you can validate query parameters and manipulate them without directly working with URLSearchParams
.
Here's an example of a route with validation and a component that updates parameters using useNavigate
:
1 // Define a route with search parameter validation2 const ProductsRoute = createFileRoute('/products')({3 validateSearch: (search) => ({ ...search, filter: search.filter ?? '' }),4 component: Products,5 });67 // Component to read and update query parameters8 function Products() {9 const { filter } = ProductsRoute.useSearch();10 const navigate = useNavigate({ from: ProductsRoute.fullPath });1112 function setActiveFilter() {13 navigate({ search: (old) => ({ ...old, filter: 'active' }) });14 }1516 return (17 <div>18 <p>Current Filter: {filter}</p>19 <button onClick={setActiveFilter}>Active Products</button>20 </div>21 );22 }
With this approach, the filter
state is stored in the URL, simplifying sharing, reloading, and collaboration.
Render Optimizations in TanStack Router
TanStack Router incorporates advanced optimization techniques to minimize unnecessary re-renders. Key mechanisms include:
1. Structural Sharing
When search parameters update, TanStack Router preserves references to unchanged parts. For instance, if foo
and bar
exist in the search parameters and only bar
changes, the reference to foo
remains the same, reducing re-renders.
2. Fine-Grained Selectors
You can subscribe to specific parts of the state using the select
option in hooks. This minimizes re-renders to only when the selected field changes.
1 // Component re-renders only if `foo` changes2 const foo = Route.useSearch({ select: ({ foo }) => foo });
If bar
changes while foo
remains unchanged, the component will not re-render.
3. Structural Sharing with Select
When using select
, returning a new object each time can cause unnecessary re-renders. Enable structural sharing to let TanStack Router reuse unchanged objects.
Enable globally when creating the router:
1 const router = createRouter({2 routeTree,3 defaultStructuralSharing: true,4 });
Or enable it locally:
1 const result = Route.useSearch({2 select: (search) => ({3 foo: search.foo,4 greeting: `Hello, ${search.foo}`,5 }),6 structuralSharing: true,7 });
Note: Data must be JSON-compatible (e.g., primitives, plain objects, arrays). Non-JSON-compatible data like new Date()
will not support structural sharing, and the compiler will warn you.
Devtools
To understand how the router selects routes, loads data, or handles caching and errors, you can use TanStack Router Devtools. These tools display the current route tree, cache state, asynchronous data loads, and errors. Here's an example of how to set them up:
1 import { TanStackRouterDevtools } from '@tanstack/router-devtools';23 function App() {4 return (5 <>6 <RouterProvider router={router} />7 <TanStackRouterDevtools router={router} />8 </>9 );10 }
The Devtools panel can be toggled open or closed, and its state (open/closed) is stored in localStorage
for persistence.
Additional Features
Here’s a summary of advanced capabilities that might be useful for specific scenarios (detailed explanations are available in the official TanStack Router documentation):
1. Virtual File Routes
If regular file-based routing doesn't meet your needs, you can programmatically create route trees. This allows you to combine real files and directories under specific paths.
- Use cases: Combine virtual and file-based routes or mount standard route folders to custom URLs.
2. Server-Side Rendering (SSR) and Streaming
TanStack Router supports both non-streaming and streaming SSR:
- Non-streaming SSR: Load data, generate HTML, and hydrate the fully-rendered page on the client.
- Streaming SSR: Deliver critical content first for faster rendering while loading heavy data in parallel, improving perceived speed.
3. Route Masking
Route masking lets you display one URL to users while routing internally to a different path. Useful for hiding certain details in the URL, such as modal parameters or service-specific queries.
4. Authenticated Routes
Use the beforeLoad
function to check user authentication. Redirect unauthorized users to a login page or other content. TanStack Router integrates well with React contexts or state management solutions for handling authentication.
5. Data Mutations
For managing mutations, consider using external tools like TanStack Query or SWR, combined with the router’s cache. After a mutation, call router.invalidate()
to refetch updated data seamlessly.
6. Scroll Restoration
TanStack Router simplifies scroll restoration for both entire pages and specific scroll containers. It eliminates the need for custom solutions to manage scroll positions during navigation.
Devtools To understand how the router selects routes, loads data, or handles caching and errors, you can use TanStack Router Devtools. These tools display the current route tree, cache state, asynchronous data loads, and errors. Here's an example of how to set them up:
1 import { TanStackRouterDevtools } from '@tanstack/router-devtools';23 function App() {4 return (5 <>6 <RouterProvider router={router} />7 <TanStackRouterDevtools router={router} />8 </>9 );10 }
The Devtools panel can be toggled open or closed, and its state (open/closed) is stored in localStorage for persistence.
Additional Features
Here’s a summary of advanced capabilities that might be useful for specific scenarios (detailed explanations are available in the official TanStack Router documentation):
1. Virtual File Routes
If regular file-based routing doesn't meet your needs, you can programmatically create route trees. This allows you to combine real files and directories under specific paths.
- Use cases: Combine virtual and file-based routes or mount standard route folders to custom URLs.
2. Server-Side Rendering (SSR) and Streaming
TanStack Router supports both non-streaming and streaming SSR:
- Non-streaming SSR: Load data, generate HTML, and hydrate the fully-rendered page on the client.
- Streaming SSR: Deliver critical content first for faster rendering while loading heavy data in parallel, improving perceived speed.
3. Route Masking
Route masking lets you display one URL to users while routing internally to a different path. Useful for hiding certain details in the URL, such as modal parameters or service-specific queries.
4. Authenticated Routes
Use the beforeLoad function to check user authentication. Redirect unauthorized users to a login page or other content. TanStack Router integrates well with React contexts or state management solutions for handling authentication.
5. Data Mutations
For managing mutations, consider using external tools like TanStack Query or SWR, combined with the router’s cache. After a mutation, call router.invalidate()
to refetch updated data seamlessly.
6. Scroll Restoration
TanStack Router simplifies scroll restoration for both entire pages and specific scroll containers. It eliminates the need for custom solutions to manage scroll positions during navigation.
7. Flexible Error Handling
In addition to classic boundary components (errorComponent
, notFoundComponent
), you can throw notFound()
from loaders or components. TanStack Router will delegate to the nearest “Not Found” handler automatically.
8. External Data Loading
For advanced caching or data fetching, TanStack Router integrates smoothly with TanStack Query, RTK Query, SWR, or Relay. You can pass these clients through the router context and use loaders to prefetch data before rendering.
These features make TanStack Router a versatile and powerful tool for handling even the most complex routing scenarios in modern web applications.
Why Choose TanStack Router?
Ideal for projects emphasizing type safety, optimized data fetching, and query management, TanStack Router complements tools like TanStack Query for a seamless development experience.
By embracing modern practices and offering unmatched flexibility, TanStack Router is a game-changer in React routing.
Conclusion
TanStack Router offers a modern, type-safe approach to building robust routing systems in React applications. With features like data loaders, search parameter validation, SSR support, and built-in optimization mechanisms, it simplifies the development of dynamic, scalable applications. Its seamless integration with tools like React Query, flexible error handling, and scroll restoration ensures a smooth developer experience. Whether you're building simple SPAs or complex enterprise-level applications, TanStack Router provides the tools and flexibility needed to manage routes efficiently. Start exploring its capabilities today to unlock the full potential of your React projects.