Skip to content

Commit cd64af9

Browse files
authored
Merge pull request #11151 from marmelab/doc-tanstack-start
[Doc]: Add TanStack Start section
2 parents 219629b + 9f8af1c commit cd64af9

4 files changed

Lines changed: 250 additions & 0 deletions

File tree

docs/TanStackStart.md

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
---
2+
layout: default
3+
title: "TanStack Start Integration"
4+
---
5+
6+
# TanStack Start Integration
7+
8+
[TanStack Start](https://tanstack.com/start) is a full-stack React framework built on top of TanStack Router. React-admin supports [TanStack Router](https://tanstack.com/router/latest) as an alternative to react-router, so you can use react-admin in a TanStack Start application.
9+
10+
## Setting Up TanStack Start
11+
12+
Let's start by creating a new TanStack Start project. Run the following command:
13+
14+
```bash
15+
npm create @tanstack/start@latest
16+
# or
17+
yarn create @tanstack/start
18+
```
19+
20+
This script will ask you for more details about your project. The prompts include:
21+
22+
- What would you like to name your project? (example: `tanstack-admin`)
23+
- Select toolchain
24+
- Select deployment adapter
25+
- What add-ons would you like for your project?
26+
- Would you like an example?
27+
- Initialize git repository
28+
- Install dependencies
29+
30+
For this tutorial, we used the following choices in our local setup: `ESLint` toolchain, `Nitro (agnostic)` deployment adapter, `Query` add-on, and `None` for the example. You can pick different options depending on your needs.
31+
32+
If you want to see the current list of add-ons, run:
33+
34+
```bash
35+
npm create @tanstack/start@latest --list-add-ons
36+
# or
37+
yarn create @tanstack/start --list-add-ons
38+
```
39+
40+
The project structure should look like this:
41+
42+
![TanStack Start project structure](./img/tanstack-structure.png)
43+
44+
## Setting Up React-Admin In TanStack Start
45+
46+
Add the `react-admin` and `ra-router-tanstack` packages, as well as a data provider package. In this example, we'll use `ra-data-json-server` to connect to a test API provided by [JSONPlaceholder](https://jsonplaceholder.typicode.com).
47+
48+
```bash
49+
cd tanstack-admin
50+
npm install react-admin ra-router-tanstack ra-data-json-server
51+
# or
52+
yarn add react-admin ra-router-tanstack ra-data-json-server
53+
```
54+
55+
## Adding React-Admin In A Sub Route
56+
57+
TanStack Start uses file-based routing. React-admin generates dynamic routes for each resource (e.g. `/admin/users`, `/admin/users/:id`). To avoid “Not Found” pages, we create explicit routes for the resource paths and render the same admin component in each route.
58+
59+
First, create the admin route at `src/routes/admin.tsx`:
60+
61+
```tsx
62+
// in src/routes/admin.tsx
63+
import { createFileRoute } from '@tanstack/react-router';
64+
import {
65+
Admin,
66+
Resource,
67+
ListGuesser,
68+
EditGuesser,
69+
} from 'react-admin';
70+
import jsonServerProvider from 'ra-data-json-server';
71+
import { tanStackRouterProvider } from 'ra-router-tanstack';
72+
73+
const dataProvider = jsonServerProvider('https://jsonplaceholder.typicode.com');
74+
export const Route = createFileRoute('/admin')({ component: App });
75+
76+
function App() {
77+
return (
78+
<Admin
79+
dataProvider={dataProvider}
80+
routerProvider={tanStackRouterProvider}
81+
basename="/admin"
82+
>
83+
<Resource
84+
name="users"
85+
list={ListGuesser}
86+
edit={EditGuesser}
87+
/>
88+
</Admin>
89+
);
90+
}
91+
92+
export default App;
93+
```
94+
95+
Then create the routes for the resource and its dynamic paths:
96+
97+
```tsx
98+
// in src/routes/index.tsx
99+
import { createFileRoute, Link } from '@tanstack/react-router';
100+
101+
function Home() {
102+
return (
103+
<div>
104+
<h1>TanStack Start + React-Admin</h1>
105+
<p>
106+
Go to the admin at <Link to="/admin">/admin</Link>
107+
</p>
108+
</div>
109+
);
110+
}
111+
112+
export const Route = createFileRoute('/')({ component: Home });
113+
```
114+
115+
```tsx
116+
// in src/routes/admin.users.tsx
117+
import { createFileRoute } from '@tanstack/react-router';
118+
import App from './admin';
119+
120+
export const Route = createFileRoute('/admin/users')({ component: App });
121+
```
122+
123+
```tsx
124+
// in src/routes/admin.users.$id.tsx
125+
import { createFileRoute } from '@tanstack/react-router';
126+
import App from './admin';
127+
128+
export const Route = createFileRoute('/admin/users/$id')({ component: App });
129+
```
130+
131+
132+
You can now start the app in `development` mode:
133+
134+
```bash
135+
npm run dev
136+
# or
137+
yarn dev
138+
```
139+
140+
The admin should render at `/admin` on your dev server.
141+
142+
![TanStack Start admin screen](./img/tanstack-admin.png)
143+
144+
**Tip**: If you add more resources, create matching file-based routes for each resource list and edit path under `/admin` and point them to the same `App` component.
145+
146+
## Removing The TanStack Header
147+
148+
TanStack Start adds a default header in the root layout. If you want the admin to render without that header, edit `src/routes/__root.tsx` and remove the `<Header />` component from `RootDocument`.
149+
150+
## Adding an API
151+
152+
TanStack Start doesn't force a specific backend. If you need an API for your admin, you can:
153+
154+
- Use an external CRUD backend like Supabase or PostgREST
155+
- Proxy requests through your app's server runtime (if your deployment includes one)
156+
157+
For example, you can create a REST API with Supabase and proxy it through TanStack Start so you don't expose service keys to the browser.
158+
159+
First, create a Supabase REST API and its associated PostgreSQL database directly on the [Supabase website](https://app.supabase.com/) (it's free for tests and low usage). Once the setup is finished, use the Supabase manager to add the following table:
160+
161+
- `users` with fields: `id`, `name`, and `email`
162+
163+
You can populate these tables via the Supabase UI if you want.
164+
165+
Supabase exposes a REST API at `https://YOUR_INSTANCE.supabase.co/rest/v1`.
166+
167+
Next, create a configuration to let the TanStack Start app connect to Supabase. Create a `.env` file:
168+
169+
```sh
170+
# In `.env`
171+
SUPABASE_URL="https://MY_INSTANCE.supabase.co"
172+
SUPABASE_SERVICE_ROLE="MY_SERVICE_ROLE_KEY"
173+
```
174+
175+
**Tip**: This example uses the **service role key** here and not the anonymous role. This allows mutations without dealing with authorization. **You shouldn't do this in production**, but use the [Supabase authorization](https://supabase.com/docs/guides/auth) feature instead.
176+
177+
Time to bootstrap the API Proxy. Create a new TanStack Start route at `src/routes/api.$.ts`:
178+
179+
```ts
180+
// in src/routes/api.$.ts
181+
import { createFileRoute } from '@tanstack/react-router'
182+
183+
export const Route = createFileRoute('/api/$')({
184+
server: {
185+
handlers: {
186+
GET: ({ request }) => proxy(request),
187+
POST: ({ request }) => proxy(request),
188+
PUT: ({ request }) => proxy(request),
189+
DELETE: ({ request }) => proxy(request),
190+
},
191+
},
192+
})
193+
194+
const API_PREFIX = '/api'
195+
196+
const proxy = (request: Request) => {
197+
const apiUrl = getSupabaseUrlFromRequestUrl(request.url)
198+
199+
return fetch(apiUrl, {
200+
method: request.method,
201+
body: request.body,
202+
headers: {
203+
prefer: request.headers.get('prefer') ?? '',
204+
accept: request.headers.get('accept') ?? 'application/json',
205+
'Accept-Encoding': '',
206+
apiKey: `${process.env.SUPABASE_SERVICE_ROLE}`,
207+
Authorization: `Bearer ${process.env.SUPABASE_SERVICE_ROLE}`,
208+
},
209+
})
210+
}
211+
212+
const getSupabaseUrlFromRequestUrl = (url: string) => {
213+
const startOfRequest = url.indexOf(API_PREFIX)
214+
const query = url.substring(startOfRequest + API_PREFIX.length)
215+
return `${process.env.SUPABASE_URL}/rest/v1${query}`
216+
}
217+
```
218+
219+
**Tip**: Some of this code is PostgREST-specific. The `prefer` header is required to let PostgREST return one record instead of an array containing one record in response to `getOne` requests. A proxy for another CRUD API will require different parameters.
220+
221+
Update the react-admin data provider to use the Supabase adapter instead of the JSON Server one. As Supabase provides a PostgREST endpoint, we'll use [`ra-data-postgrest`](https://github.com/raphiniert-com/ra-data-postgrest):
222+
223+
```sh
224+
npm install @raphiniert/ra-data-postgrest
225+
# or
226+
yarn add @raphiniert/ra-data-postgrest
227+
```
228+
229+
Finally, update your Admin dataProvider:
230+
231+
```tsx
232+
// in src/routes/admin.tsx
233+
import { Admin, Resource, ListGuesser, fetchUtils } from 'react-admin';
234+
import postgrestRestProvider from '@raphiniert/ra-data-postgrest';
235+
import { tanStackRouterProvider } from 'ra-router-tanstack';
236+
237+
const dataProvider = postgrestRestProvider({
238+
apiUrl: '/api',
239+
httpClient: fetchUtils.fetchJson,
240+
defaultListOp: 'eq',
241+
schema: () => '',
242+
});
243+
244+
// ... keep the rest of the file the same
245+
```
246+
247+
## Next Steps
248+
249+
For standalone vs embedded setups, see the [TanStack Router Integration](./TanStackRouter.md).

docs/img/tanstack-admin.png

64.2 KB
Loading

docs/img/tanstack-structure.png

7.74 KB
Loading

docs/navigation.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<li {% if page.path == 'Remix.md' %} class="active" {% endif %}><a class="nav-link" href="./Remix.html">Remix</a></li>
1717
<li {% if page.path == 'ReactRouterFramework.md' %} class="active" {% endif %}><a class="nav-link" href="./ReactRouterFramework.html">React Router Framework</a></li>
1818
<li {% if page.path == 'Deploy.md' %} class="active beginner" {% endif %}><a class="nav-link" href="./Deploy.html">Deployment</a></li>
19+
<li {% if page.path == 'TanStackStart.md' %} class="active" {% endif %}><a class="nav-link" href="./TanStackStart.html">TanStack Start</a></li>
1920
</ul>
2021

2122
<ul><div>Guides & Concepts</div>

0 commit comments

Comments
 (0)