TanStack Router Integration
React-admin supports TanStack Router as an alternative to react-router. This allows you to use react-admin in a TanStack Start application.
Installation
To use TanStack Router with react-admin, install the required packages:
npm install ra-router-tanstack @tanstack/react-router @tanstack/history
# or
yarn add ra-router-tanstack @tanstack/react-router @tanstack/history
Configuration
To use TanStack Router, set the <Admin routerProvider> to tanStackRouterProvider:
import { Admin, Resource, ListGuesser } from 'react-admin';
import { tanStackRouterProvider } from 'ra-router-tanstack';
import { dataProvider } from './dataProvider';
const App = () => (
<Admin
dataProvider={dataProvider}
routerProvider={tanStackRouterProvider}
>
<Resource name="posts" list={ListGuesser} />
<Resource name="comments" list={ListGuesser} />
</Admin>
);
export default App;
That’s it! React-admin will now use TanStack Router for all routing operations.
Standalone Mode
When using tanStackRouterProvider without an existing TanStack Router, react-admin creates its own router automatically. This is called standalone mode.
In standalone mode, react-admin:
- Creates a TanStack Router with hash-based history (URLs like
/#/posts) - Handles all route matching internally
- Manages navigation and history
This is the simplest setup and requires no additional configuration.
// Standalone mode - react-admin creates the router
import { Admin, Resource } from 'react-admin';
import { tanStackRouterProvider } from 'ra-router-tanstack';
const App = () => (
<Admin
dataProvider={dataProvider}
routerProvider={tanStackRouterProvider}
>
<Resource name="posts" list={PostList} />
</Admin>
);
Embedded Mode
If your application already uses TanStack Router, you can embed react-admin inside it. React-admin detects the existing router context and uses it instead of creating its own.
import * as React from 'react';
import {
createRouter,
createRootRoute,
createRoute,
RouterProvider,
Outlet,
Link,
} from '@tanstack/react-router';
import { createHashHistory } from '@tanstack/history';
import { Admin, Resource } from 'react-admin';
import { tanStackRouterProvider } from 'ra-router-tanstack';
import { dataProvider } from './dataProvider';
import { PostList, PostEdit } from './posts';
// Define your routes
const rootRoute = createRootRoute({
component: () => (
<div>
<nav>
<Link to="/">Home</Link>
<Link to="/admin">Admin</Link>
</nav>
<Outlet />
</div>
),
});
const homeRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/',
component: () => <div>Welcome to my app!</div>,
});
// Mount react-admin at /admin
const adminRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/admin',
component: () => (
<Admin
dataProvider={dataProvider}
routerProvider={tanStackRouterProvider}
basename="/admin"
>
<Resource name="posts" list={PostList} edit={PostEdit} />
</Admin>
),
});
const routeTree = rootRoute.addChildren([homeRoute, adminRoute]);
const router = createRouter({
routeTree,
history: createHashHistory(),
});
const App = () => <RouterProvider router={router} />;
export default App;
Important: When embedding react-admin, set the basename prop to match the path where react-admin is mounted. In the example above, react-admin is mounted at /admin, so basename="/admin".
Custom Routes
You can use <CustomRoutes> to add custom pages. Use the Route component from tanStackRouterProvider to define routes:
import { Admin, Resource, CustomRoutes } from 'react-admin';
import { tanStackRouterProvider } from 'ra-router-tanstack';
const { Route } = tanStackRouterProvider;
const App = () => (
<Admin
dataProvider={dataProvider}
routerProvider={tanStackRouterProvider}
>
<CustomRoutes>
<Route path="/settings" element={<Settings />} />
<Route path="/profile" element={<Profile />} />
</CustomRoutes>
<CustomRoutes noLayout>
<Route path="/public" element={<PublicPage />} />
</CustomRoutes>
<Resource name="posts" list={PostList} />
</Admin>
);
Using Router Hooks
When using TanStack Router, import routing hooks from react-admin instead of directly from TanStack Router:
// Recommended - router-agnostic
import { useNavigate, useLocation, useParams } from 'react-admin';
The hooks from react-admin work with both react-router and TanStack Router, making your code portable:
import { useNavigate, useLocation, useParams } from 'react-admin';
const MyComponent = () => {
const navigate = useNavigate();
const location = useLocation();
const { id } = useParams();
const handleClick = () => {
navigate('/posts');
// or navigate(-1) to go back
// or navigate({ pathname: '/posts', search: '?filter=active' })
};
return (
<div>
<p>Current path: {location.pathname}</p>
<p>Record ID: {id}</p>
<button onClick={handleClick}>Go to Posts</button>
</div>
);
};
Navigation Blocking
TanStack Router supports navigation blocking out of the box. The warnWhenUnsavedChanges feature in react-admin forms works automatically:
import { Edit, SimpleForm, TextInput } from 'react-admin';
const PostEdit = () => (
<Edit>
<SimpleForm warnWhenUnsavedChanges>
<TextInput source="title" />
<TextInput source="body" multiline />
</SimpleForm>
</Edit>
);
Unlike react-router (which requires a Data Router for blocking to work), TanStack Router always supports navigation blocking.
Linking Between Pages
Use the LinkBase component from react-admin for router-agnostic links:
import { LinkBase } from 'react-admin';
const Dashboard = () => (
<div>
<h1>Dashboard</h1>
<LinkBase to="/posts">View all posts</LinkBase>
<LinkBase to="/posts/create">Create a new post</LinkBase>
<LinkBase to="/posts/123/show">View post #123</LinkBase>
</div>
);
Or use useCreatePath for dynamic paths:
import { LinkBase, useCreatePath } from 'react-admin';
const Dashboard = () => {
const createPath = useCreatePath();
return (
<div>
<LinkBase to={createPath({ resource: 'posts', type: 'list' })}>
Posts
</LinkBase>
<LinkBase to={createPath({ resource: 'posts', type: 'create' })}>
Create Post
</LinkBase>
<LinkBase to={createPath({ resource: 'posts', type: 'show', id: 123 })}>
Post #123
</LinkBase>
</div>
);
};
Limitations
The TanStack Router adapter has some limitations compared to native TanStack Router usage:
Type Safety
TanStack Router’s main feature is compile-time type safety based on route definitions. The react-admin adapter doesn’t provide this level of type safety because react-admin generates routes dynamically from <Resource> components.
Search Params
TanStack Router treats search params as typed objects with validation. The adapter uses string-based search (?key=value) for compatibility with react-admin’s list filters.
Route Loaders
TanStack Router’s data loading features (loader, beforeLoad) are not used by the adapter. React-admin handles data loading through its own dataProvider system.
File-Based Routing
TanStack Router supports file-based routing similar to Next.js. This feature is not compatible with react-admin’s declarative <Resource> approach.
