@tanstack/react-start 1.166.14 → 1.166.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/intent.js ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+ // Auto-generated by @tanstack/intent setup
3
+ // Exposes the intent end-user CLI for consumers of this library.
4
+ // Commit this file, then add to your package.json:
5
+ // "bin": { "intent": "./bin/intent.js" }
6
+ try {
7
+ await import('@tanstack/intent/intent-library')
8
+ } catch (e) {
9
+ const isModuleNotFound =
10
+ e?.code === 'ERR_MODULE_NOT_FOUND' || e?.code === 'MODULE_NOT_FOUND'
11
+ const missingIntentLibrary =
12
+ typeof e?.message === 'string' && e.message.includes('@tanstack/intent')
13
+
14
+ if (isModuleNotFound && missingIntentLibrary) {
15
+ console.error('@tanstack/intent is not installed.')
16
+ console.error('')
17
+ console.error('Install it as a dev dependency:')
18
+ console.error(' npm add -D @tanstack/intent')
19
+ console.error('')
20
+ console.error('Or run directly:')
21
+ console.error(' npx @tanstack/intent@latest list')
22
+ process.exit(1)
23
+ }
24
+ throw e
25
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-start",
3
- "version": "1.166.14",
3
+ "version": "1.166.15",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -91,26 +91,35 @@
91
91
  "sideEffects": false,
92
92
  "files": [
93
93
  "dist",
94
- "src"
94
+ "src",
95
+ "skills",
96
+ "bin",
97
+ "!skills/_artifacts"
95
98
  ],
96
99
  "engines": {
97
100
  "node": ">=22.12.0"
98
101
  },
99
102
  "dependencies": {
100
103
  "pathe": "^2.0.3",
101
- "@tanstack/react-start-client": "1.166.12",
102
- "@tanstack/react-start-server": "1.166.12",
104
+ "@tanstack/react-start-client": "1.166.13",
105
+ "@tanstack/react-start-server": "1.166.13",
103
106
  "@tanstack/router-utils": "^1.161.6",
104
- "@tanstack/start-plugin-core": "1.166.14",
105
- "@tanstack/react-router": "1.167.3",
106
- "@tanstack/start-client-core": "1.166.11",
107
- "@tanstack/start-server-core": "1.166.11"
107
+ "@tanstack/start-plugin-core": "1.166.15",
108
+ "@tanstack/react-router": "1.167.4",
109
+ "@tanstack/start-client-core": "1.166.12",
110
+ "@tanstack/start-server-core": "1.166.12"
108
111
  },
109
112
  "peerDependencies": {
110
113
  "react": ">=18.0.0 || >=19.0.0",
111
114
  "react-dom": ">=18.0.0 || >=19.0.0",
112
115
  "vite": ">=7.0.0"
113
116
  },
117
+ "devDependencies": {
118
+ "@tanstack/intent": "^0.0.14"
119
+ },
120
+ "bin": {
121
+ "intent": "./bin/intent.js"
122
+ },
114
123
  "scripts": {
115
124
  "clean": "rimraf ./dist && rimraf ./coverage",
116
125
  "test": "pnpm test:build",
@@ -0,0 +1,435 @@
1
+ ---
2
+ name: lifecycle/migrate-from-nextjs
3
+ description: >-
4
+ Step-by-step migration from Next.js App Router to TanStack Start:
5
+ route definition conversion, API mapping, server function
6
+ conversion from Server Actions, middleware conversion, data
7
+ fetching pattern changes.
8
+ type: lifecycle
9
+ library: tanstack-start
10
+ library_version: '1.166.2'
11
+ requires:
12
+ - start-core
13
+ - react-start
14
+ sources:
15
+ - TanStack/router:docs/start/framework/react/guide/server-functions.md
16
+ - TanStack/router:docs/start/framework/react/guide/middleware.md
17
+ - TanStack/router:docs/start/framework/react/guide/execution-model.md
18
+ ---
19
+
20
+ # Migrate from Next.js App Router to TanStack Start
21
+
22
+ This is a step-by-step migration checklist. Complete tasks in order.
23
+
24
+ > **CRITICAL**: TanStack Start is isomorphic by default. ALL code runs in both environments unless you use `createServerFn`. This is the opposite of Next.js Server Components, where code is server-only by default.
25
+
26
+ > **CRITICAL**: TanStack Start uses `createServerFn`, NOT `"use server"` directives. Do not carry over any `"use server"` or `"use client"` directives.
27
+
28
+ > **CRITICAL**: Types are FULLY INFERRED in TanStack Router/Start. Never cast, never annotate inferred values.
29
+
30
+ ## Pre-Migration
31
+
32
+ - [ ] **Create a migration branch**
33
+
34
+ ```bash
35
+ git checkout -b migrate-to-tanstack-start
36
+ ```
37
+
38
+ - [ ] **Install TanStack Start**
39
+
40
+ ```bash
41
+ npm i @tanstack/react-start @tanstack/react-router
42
+ npm i -D vite @vitejs/plugin-react
43
+ ```
44
+
45
+ - [ ] **Remove Next.js**
46
+
47
+ ```bash
48
+ npm uninstall next @next/font @next/image
49
+ ```
50
+
51
+ ## Concept Mapping
52
+
53
+ | Next.js App Router | TanStack Start |
54
+ | -------------------------------- | ------------------------------------------------------------------------- |
55
+ | `app/page.tsx` | `src/routes/index.tsx` |
56
+ | `app/layout.tsx` | `src/routes/__root.tsx` |
57
+ | `app/posts/[id]/page.tsx` | `src/routes/posts/$postId.tsx` |
58
+ | `app/api/users/route.ts` | `src/routes/api/users.ts` (server property) |
59
+ | `"use server"` + Server Actions | `createServerFn()` |
60
+ | `"use client"` | Not needed (everything is isomorphic) |
61
+ | Server Components (default) | All components are isomorphic; use `createServerFn` for server-only logic |
62
+ | `next/navigation` `useRouter` | `useRouter()` from `@tanstack/react-router` |
63
+ | `next/link` `Link` | `<Link>` from `@tanstack/react-router` |
64
+ | `next/head` or `metadata` export | `head` property on route |
65
+ | `middleware.ts` (edge) | `createMiddleware()` in `src/start.ts` |
66
+ | `next.config.js` | `vite.config.ts` with `tanstackStart()` |
67
+ | `generateStaticParams` | `prerender` config in `vite.config.ts` |
68
+
69
+ ## Step 1: Vite Configuration
70
+
71
+ Replace `next.config.js` with:
72
+
73
+ ```ts
74
+ // vite.config.ts
75
+ import { defineConfig } from 'vite'
76
+ import { tanstackStart } from '@tanstack/react-start/plugin/vite'
77
+ import viteReact from '@vitejs/plugin-react'
78
+
79
+ export default defineConfig({
80
+ plugins: [
81
+ tanstackStart(), // MUST come before react()
82
+ viteReact(),
83
+ ],
84
+ })
85
+ ```
86
+
87
+ Update `package.json`:
88
+
89
+ ```json
90
+ {
91
+ "type": "module",
92
+ "scripts": {
93
+ "dev": "vite dev",
94
+ "build": "vite build",
95
+ "start": "node .output/server/index.mjs"
96
+ }
97
+ }
98
+ ```
99
+
100
+ ## Step 2: Router Factory
101
+
102
+ ```tsx
103
+ // src/router.tsx
104
+ import { createRouter } from '@tanstack/react-router'
105
+ import { routeTree } from './routeTree.gen'
106
+
107
+ export function getRouter() {
108
+ const router = createRouter({
109
+ routeTree,
110
+ scrollRestoration: true,
111
+ })
112
+ return router
113
+ }
114
+ ```
115
+
116
+ ## Step 3: Convert Layout → Root Route
117
+
118
+ Next.js:
119
+
120
+ ```tsx
121
+ // app/layout.tsx
122
+ export const metadata = { title: 'My App' }
123
+ export default function RootLayout({ children }) {
124
+ return (
125
+ <html>
126
+ <body>{children}</body>
127
+ </html>
128
+ )
129
+ }
130
+ ```
131
+
132
+ TanStack Start:
133
+
134
+ ```tsx
135
+ // src/routes/__root.tsx
136
+ import type { ReactNode } from 'react'
137
+ import {
138
+ Outlet,
139
+ createRootRoute,
140
+ HeadContent,
141
+ Scripts,
142
+ } from '@tanstack/react-router'
143
+
144
+ export const Route = createRootRoute({
145
+ head: () => ({
146
+ meta: [
147
+ { charSet: 'utf-8' },
148
+ { name: 'viewport', content: 'width=device-width, initial-scale=1' },
149
+ { title: 'My App' },
150
+ ],
151
+ }),
152
+ component: RootComponent,
153
+ })
154
+
155
+ function RootComponent() {
156
+ return (
157
+ <html>
158
+ <head>
159
+ <HeadContent />
160
+ </head>
161
+ <body>
162
+ <Outlet />
163
+ <Scripts />
164
+ </body>
165
+ </html>
166
+ )
167
+ }
168
+ ```
169
+
170
+ ## Step 4: Convert Pages → File Routes
171
+
172
+ Next.js:
173
+
174
+ ```tsx
175
+ // app/posts/[id]/page.tsx
176
+ export default function PostPage({ params }: { params: { id: string } }) {
177
+ // ...
178
+ }
179
+ ```
180
+
181
+ TanStack Start:
182
+
183
+ ```tsx
184
+ // src/routes/posts/$postId.tsx
185
+ import { createFileRoute } from '@tanstack/react-router'
186
+
187
+ export const Route = createFileRoute('/posts/$postId')({
188
+ component: PostPage,
189
+ })
190
+
191
+ function PostPage() {
192
+ const { postId } = Route.useParams()
193
+ // ...
194
+ }
195
+ ```
196
+
197
+ Key differences:
198
+
199
+ - Dynamic segments use `$param` not `[param]`
200
+ - Params accessed via `Route.useParams()` not component props
201
+ - Route path in filename uses `.` or `/` separators
202
+
203
+ ## Step 5: Convert Server Actions → Server Functions
204
+
205
+ Next.js:
206
+
207
+ ```tsx
208
+ // app/actions.ts
209
+ 'use server'
210
+ export async function createPost(formData: FormData) {
211
+ const title = formData.get('title') as string
212
+ await db.posts.create({ title })
213
+ }
214
+ ```
215
+
216
+ TanStack Start:
217
+
218
+ ```tsx
219
+ // src/utils/posts.functions.ts
220
+ import { createServerFn } from '@tanstack/react-start'
221
+
222
+ export const createPost = createServerFn({ method: 'POST' })
223
+ .inputValidator((data) => {
224
+ if (!(data instanceof FormData)) throw new Error('Expected FormData')
225
+ return { title: data.get('title')?.toString() || '' }
226
+ })
227
+ .handler(async ({ data }) => {
228
+ await db.posts.create({ title: data.title })
229
+ return { success: true }
230
+ })
231
+ ```
232
+
233
+ ## Step 6: Convert Data Fetching
234
+
235
+ Next.js Server Component:
236
+
237
+ ```tsx
238
+ // app/posts/page.tsx (Server Component — server-only by default)
239
+ export default async function PostsPage() {
240
+ const posts = await db.posts.findMany()
241
+ return <PostList posts={posts} />
242
+ }
243
+ ```
244
+
245
+ TanStack Start:
246
+
247
+ ```tsx
248
+ // src/routes/posts.tsx
249
+ import { createFileRoute } from '@tanstack/react-router'
250
+ import { createServerFn } from '@tanstack/react-start'
251
+
252
+ const getPosts = createServerFn({ method: 'GET' }).handler(async () => {
253
+ return db.posts.findMany()
254
+ })
255
+
256
+ export const Route = createFileRoute('/posts')({
257
+ loader: () => getPosts(), // loader is isomorphic, getPosts runs on server
258
+ component: PostsPage,
259
+ })
260
+
261
+ function PostsPage() {
262
+ const posts = Route.useLoaderData()
263
+ return <PostList posts={posts} />
264
+ }
265
+ ```
266
+
267
+ ## Step 7: Convert API Routes → Server Routes
268
+
269
+ Next.js:
270
+
271
+ ```ts
272
+ // app/api/users/route.ts
273
+ export async function GET() {
274
+ const users = await db.users.findMany()
275
+ return Response.json(users)
276
+ }
277
+ ```
278
+
279
+ TanStack Start:
280
+
281
+ ```ts
282
+ // src/routes/api/users.ts
283
+ import { createFileRoute } from '@tanstack/react-router'
284
+
285
+ export const Route = createFileRoute('/api/users')({
286
+ server: {
287
+ handlers: {
288
+ GET: async () => {
289
+ const users = await db.users.findMany()
290
+ return Response.json(users)
291
+ },
292
+ },
293
+ },
294
+ })
295
+ ```
296
+
297
+ ## Step 8: Convert Navigation
298
+
299
+ Next.js:
300
+
301
+ ```tsx
302
+ import Link from 'next/link'
303
+ ;<Link href={`/posts/${post.id}`}>View Post</Link>
304
+ ```
305
+
306
+ TanStack Start:
307
+
308
+ ```tsx
309
+ import { Link } from '@tanstack/react-router'
310
+ ;<Link to="/posts/$postId" params={{ postId: post.id }}>
311
+ View Post
312
+ </Link>
313
+ ```
314
+
315
+ Never interpolate params into the `to` string. Use `params` prop.
316
+
317
+ ## Step 9: Convert Middleware
318
+
319
+ Next.js:
320
+
321
+ ```ts
322
+ // middleware.ts
323
+ export function middleware(request: NextRequest) {
324
+ const token = request.cookies.get('session')
325
+ if (!token) return NextResponse.redirect(new URL('/login', request.url))
326
+ }
327
+ export const config = { matcher: ['/dashboard/:path*'] }
328
+ ```
329
+
330
+ TanStack Start:
331
+
332
+ ```tsx
333
+ // src/start.ts — must be manually created
334
+ import { createStart, createMiddleware } from '@tanstack/react-start'
335
+ import { redirect } from '@tanstack/react-router'
336
+
337
+ const authMiddleware = createMiddleware().server(async ({ next, request }) => {
338
+ const cookie = request.headers.get('cookie')
339
+ if (!cookie?.includes('session=')) {
340
+ throw redirect({ to: '/login' })
341
+ }
342
+ return next()
343
+ })
344
+
345
+ export const startInstance = createStart(() => ({
346
+ requestMiddleware: [authMiddleware],
347
+ }))
348
+ ```
349
+
350
+ ## Step 10: Convert Metadata/SEO
351
+
352
+ Next.js:
353
+
354
+ ```tsx
355
+ export const metadata = {
356
+ title: 'Post Title',
357
+ description: 'Post description',
358
+ }
359
+ ```
360
+
361
+ TanStack Start:
362
+
363
+ ```tsx
364
+ export const Route = createFileRoute('/posts/$postId')({
365
+ loader: async ({ params }) => fetchPost(params.postId),
366
+ head: ({ loaderData }) => ({
367
+ meta: [
368
+ { title: loaderData.title },
369
+ { name: 'description', content: loaderData.excerpt },
370
+ { property: 'og:title', content: loaderData.title },
371
+ ],
372
+ }),
373
+ })
374
+ ```
375
+
376
+ ## Post-Migration Checklist
377
+
378
+ - [ ] Remove all `"use server"` and `"use client"` directives
379
+ - [ ] Remove `next.config.js` / `next.config.ts`
380
+ - [ ] Remove `app/` directory (replaced by `src/routes/`)
381
+ - [ ] Remove `middleware.ts` (replaced by `src/start.ts`)
382
+ - [ ] Verify no `next/*` imports remain
383
+ - [ ] Run `npm run dev` and check all routes
384
+ - [ ] Verify server-only code is inside `createServerFn` (not bare in components/loaders)
385
+ - [ ] Check that `<Scripts />` is in the root route `<body>`
386
+
387
+ ## Common Mistakes
388
+
389
+ ### 1. CRITICAL: Keeping Server Component mental model
390
+
391
+ ```tsx
392
+ // WRONG — treating component as server-only (Next.js habit)
393
+ function PostsPage() {
394
+ const posts = await db.posts.findMany() // fails on client
395
+ return <div>{posts.map(...)}</div>
396
+ }
397
+
398
+ // CORRECT — use server function + loader
399
+ const getPosts = createServerFn({ method: 'GET' }).handler(async () => {
400
+ return db.posts.findMany()
401
+ })
402
+
403
+ export const Route = createFileRoute('/posts')({
404
+ loader: () => getPosts(),
405
+ component: PostsPage,
406
+ })
407
+ ```
408
+
409
+ ### 2. CRITICAL: Using "use server" directive
410
+
411
+ ```tsx
412
+ // WRONG — "use server" is Next.js/React pattern
413
+ 'use server'
414
+ export async function myAction() { ... }
415
+
416
+ // CORRECT — use createServerFn
417
+ export const myAction = createServerFn({ method: 'POST' })
418
+ .handler(async () => { ... })
419
+ ```
420
+
421
+ ### 3. HIGH: Interpolating params into Link href
422
+
423
+ ```tsx
424
+ // WRONG — Next.js pattern
425
+ <Link to={`/posts/${post.id}`}>View</Link>
426
+
427
+ // CORRECT — TanStack Router pattern
428
+ <Link to="/posts/$postId" params={{ postId: post.id }}>View</Link>
429
+ ```
430
+
431
+ ## Cross-References
432
+
433
+ - [react-start](../../react-start/SKILL.md) — full React Start setup
434
+ - [start-core/server-functions](../../../../start-client-core/skills/start-core/server-functions/SKILL.md) — server function patterns
435
+ - [start-core/execution-model](../../../../start-client-core/skills/start-core/execution-model/SKILL.md) — isomorphic execution
@@ -0,0 +1,285 @@
1
+ ---
2
+ name: react-start
3
+ description: >-
4
+ React bindings for TanStack Start: createStart, StartClient,
5
+ StartServer, React-specific imports, re-exports from
6
+ @tanstack/react-router, full project setup with React, useServerFn
7
+ hook.
8
+ type: framework
9
+ library: tanstack-start
10
+ library_version: '1.166.2'
11
+ framework: react
12
+ requires:
13
+ - start-core
14
+ sources:
15
+ - TanStack/router:packages/react-start/src
16
+ - TanStack/router:docs/start/framework/react/build-from-scratch.md
17
+ ---
18
+
19
+ # React Start (`@tanstack/react-start`)
20
+
21
+ This skill builds on start-core. Read [start-core](../../../start-client-core/skills/start-core/SKILL.md) first for foundational concepts.
22
+
23
+ This skill covers the React-specific bindings, setup, and patterns for TanStack Start.
24
+
25
+ > **CRITICAL**: All code is ISOMORPHIC by default. Loaders run on BOTH server and client. Use `createServerFn` for server-only logic.
26
+
27
+ > **CRITICAL**: Do not confuse `@tanstack/react-start` with Next.js or Remix. They are completely different frameworks with different APIs.
28
+
29
+ > **CRITICAL**: Types are FULLY INFERRED. Never cast, never annotate inferred values.
30
+
31
+ ## Package API Surface
32
+
33
+ `@tanstack/react-start` re-exports everything from `@tanstack/start-client-core` plus:
34
+
35
+ - `useServerFn` — React hook for calling server functions from components
36
+
37
+ All core APIs (`createServerFn`, `createMiddleware`, `createStart`, `createIsomorphicFn`, `createServerOnlyFn`, `createClientOnlyFn`) are available from `@tanstack/react-start`.
38
+
39
+ Server utilities (`getRequest`, `getRequestHeader`, `setResponseHeader`, `setResponseHeaders`, `setResponseStatus`) are imported from `@tanstack/react-start/server`.
40
+
41
+ ## Full Project Setup
42
+
43
+ ### 1. Install Dependencies
44
+
45
+ ```bash
46
+ npm i @tanstack/react-start @tanstack/react-router react react-dom
47
+ npm i -D vite @vitejs/plugin-react typescript @types/react @types/react-dom
48
+ ```
49
+
50
+ ### 2. package.json
51
+
52
+ ```json
53
+ {
54
+ "type": "module",
55
+ "scripts": {
56
+ "dev": "vite dev",
57
+ "build": "vite build",
58
+ "start": "node .output/server/index.mjs"
59
+ }
60
+ }
61
+ ```
62
+
63
+ ### 3. tsconfig.json
64
+
65
+ ```json
66
+ {
67
+ "compilerOptions": {
68
+ "jsx": "react-jsx",
69
+ "moduleResolution": "Bundler",
70
+ "module": "ESNext",
71
+ "target": "ES2022",
72
+ "skipLibCheck": true,
73
+ "strictNullChecks": true
74
+ }
75
+ }
76
+ ```
77
+
78
+ ### 4. vite.config.ts
79
+
80
+ ```ts
81
+ import { defineConfig } from 'vite'
82
+ import { tanstackStart } from '@tanstack/react-start/plugin/vite'
83
+ import viteReact from '@vitejs/plugin-react'
84
+
85
+ export default defineConfig({
86
+ plugins: [
87
+ tanstackStart(), // MUST come before react()
88
+ viteReact(),
89
+ ],
90
+ })
91
+ ```
92
+
93
+ ### 5. Router Factory (src/router.tsx)
94
+
95
+ ```tsx
96
+ import { createRouter } from '@tanstack/react-router'
97
+ import { routeTree } from './routeTree.gen'
98
+
99
+ export function getRouter() {
100
+ const router = createRouter({
101
+ routeTree,
102
+ scrollRestoration: true,
103
+ })
104
+ return router
105
+ }
106
+ ```
107
+
108
+ ### 6. Root Route (src/routes/\_\_root.tsx)
109
+
110
+ ```tsx
111
+ import type { ReactNode } from 'react'
112
+ import {
113
+ Outlet,
114
+ createRootRoute,
115
+ HeadContent,
116
+ Scripts,
117
+ } from '@tanstack/react-router'
118
+
119
+ export const Route = createRootRoute({
120
+ head: () => ({
121
+ meta: [
122
+ { charSet: 'utf-8' },
123
+ { name: 'viewport', content: 'width=device-width, initial-scale=1' },
124
+ { title: 'My TanStack Start App' },
125
+ ],
126
+ }),
127
+ component: RootComponent,
128
+ })
129
+
130
+ function RootComponent() {
131
+ return (
132
+ <RootDocument>
133
+ <Outlet />
134
+ </RootDocument>
135
+ )
136
+ }
137
+
138
+ function RootDocument({ children }: Readonly<{ children: ReactNode }>) {
139
+ return (
140
+ <html>
141
+ <head>
142
+ <HeadContent />
143
+ </head>
144
+ <body>
145
+ {children}
146
+ <Scripts />
147
+ </body>
148
+ </html>
149
+ )
150
+ }
151
+ ```
152
+
153
+ ### 7. Index Route (src/routes/index.tsx)
154
+
155
+ ```tsx
156
+ import { createFileRoute } from '@tanstack/react-router'
157
+ import { createServerFn } from '@tanstack/react-start'
158
+
159
+ const getGreeting = createServerFn({ method: 'GET' }).handler(async () => {
160
+ return 'Hello from TanStack Start!'
161
+ })
162
+
163
+ export const Route = createFileRoute('/')({
164
+ loader: () => getGreeting(),
165
+ component: HomePage,
166
+ })
167
+
168
+ function HomePage() {
169
+ const greeting = Route.useLoaderData()
170
+ return <h1>{greeting}</h1>
171
+ }
172
+ ```
173
+
174
+ ## useServerFn Hook
175
+
176
+ Use `useServerFn` to call server functions from React components with proper integration:
177
+
178
+ ```tsx
179
+ import { createServerFn, useServerFn } from '@tanstack/react-start'
180
+
181
+ const updatePost = createServerFn({ method: 'POST' })
182
+ .inputValidator((data: { id: string; title: string }) => data)
183
+ .handler(async ({ data }) => {
184
+ await db.posts.update(data.id, { title: data.title })
185
+ return { success: true }
186
+ })
187
+
188
+ function EditPostForm({ postId }: { postId: string }) {
189
+ const updatePostFn = useServerFn(updatePost)
190
+ const [title, setTitle] = useState('')
191
+
192
+ return (
193
+ <form
194
+ onSubmit={async (e) => {
195
+ e.preventDefault()
196
+ await updatePostFn({ data: { id: postId, title } })
197
+ }}
198
+ >
199
+ <input value={title} onChange={(e) => setTitle(e.target.value)} />
200
+ <button type="submit">Save</button>
201
+ </form>
202
+ )
203
+ }
204
+ ```
205
+
206
+ ## Global Start Configuration (src/start.ts)
207
+
208
+ ```tsx
209
+ import { createStart, createMiddleware } from '@tanstack/react-start'
210
+
211
+ const requestLogger = createMiddleware().server(async ({ next, request }) => {
212
+ console.log(`${request.method} ${request.url}`)
213
+ return next()
214
+ })
215
+
216
+ export const startInstance = createStart(() => ({
217
+ requestMiddleware: [requestLogger],
218
+ }))
219
+ ```
220
+
221
+ ## React-Specific Components
222
+
223
+ All routing components from `@tanstack/react-router` work in Start:
224
+
225
+ - `<RouterProvider>` — not needed in Start (handled automatically)
226
+ - `<Outlet>` — renders matched child route
227
+ - `<Link>` — type-safe navigation
228
+ - `<Navigate>` — declarative redirect
229
+ - `<HeadContent>` — renders head tags (must be in `<head>`)
230
+ - `<Scripts>` — renders body scripts (must be in `<body>`)
231
+ - `<Await>` — renders deferred data with Suspense
232
+ - `<ClientOnly>` — renders children only after hydration
233
+ - `<CatchBoundary>` — error boundary
234
+
235
+ ## Hooks Reference
236
+
237
+ All hooks from `@tanstack/react-router` work in Start:
238
+
239
+ - `useRouter()` — router instance
240
+ - `useRouterState()` — subscribe to router state
241
+ - `useNavigate()` — programmatic navigation
242
+ - `useSearch({ from })` — validated search params
243
+ - `useParams({ from })` — path params
244
+ - `useLoaderData({ from })` — loader data
245
+ - `useMatch({ from })` — full route match
246
+ - `useRouteContext({ from })` — route context
247
+ - `Route.useLoaderData()` — typed loader data (preferred in route files)
248
+ - `Route.useSearch()` — typed search params (preferred in route files)
249
+
250
+ ## Common Mistakes
251
+
252
+ ### 1. CRITICAL: Importing from wrong package
253
+
254
+ ```tsx
255
+ // WRONG — this is the SPA router, NOT Start
256
+ import { createServerFn } from '@tanstack/react-router'
257
+
258
+ // CORRECT — server functions come from react-start
259
+ import { createServerFn } from '@tanstack/react-start'
260
+
261
+ // CORRECT — routing APIs come from react-router (re-exported by Start too)
262
+ import { createFileRoute, Link } from '@tanstack/react-router'
263
+ ```
264
+
265
+ ### 2. HIGH: Using React hooks in beforeLoad or loader
266
+
267
+ ```tsx
268
+ // WRONG — beforeLoad/loader are NOT React components
269
+ beforeLoad: () => {
270
+ const auth = useAuth() // React hook, cannot be used here
271
+ }
272
+
273
+ // CORRECT — pass state via router context
274
+ const rootRoute = createRootRouteWithContext<{ auth: AuthState }>()({})
275
+ ```
276
+
277
+ ### 3. HIGH: Missing Scripts component
278
+
279
+ Without `<Scripts />` in the root route's `<body>`, client JavaScript doesn't load and the app won't hydrate.
280
+
281
+ ## Cross-References
282
+
283
+ - [start-core](../../../start-client-core/skills/start-core/SKILL.md) — core Start concepts
284
+ - [router-core](../../../router-core/skills/router-core/SKILL.md) — routing fundamentals
285
+ - [react-router](../../../react-router/skills/react-router/SKILL.md) — React Router hooks and components