@tanstack/start-client-core 1.166.10 → 1.166.12
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 +25 -0
- package/package.json +11 -4
- package/skills/start-core/SKILL.md +210 -0
- package/skills/start-core/deployment/SKILL.md +306 -0
- package/skills/start-core/execution-model/SKILL.md +302 -0
- package/skills/start-core/middleware/SKILL.md +365 -0
- package/skills/start-core/server-functions/SKILL.md +335 -0
- package/skills/start-core/server-routes/SKILL.md +280 -0
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/start-client-core",
|
|
3
|
-
"version": "1.166.
|
|
3
|
+
"version": "1.166.12",
|
|
4
4
|
"description": "Modern and scalable routing for React applications",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -57,7 +57,10 @@
|
|
|
57
57
|
"sideEffects": false,
|
|
58
58
|
"files": [
|
|
59
59
|
"dist",
|
|
60
|
-
"src"
|
|
60
|
+
"src",
|
|
61
|
+
"skills",
|
|
62
|
+
"bin",
|
|
63
|
+
"!skills/_artifacts"
|
|
61
64
|
],
|
|
62
65
|
"engines": {
|
|
63
66
|
"node": ">=22.12.0"
|
|
@@ -66,13 +69,17 @@
|
|
|
66
69
|
"seroval": "^1.4.2",
|
|
67
70
|
"tiny-invariant": "^1.3.3",
|
|
68
71
|
"tiny-warning": "^1.0.3",
|
|
69
|
-
"@tanstack/router-core": "1.167.2",
|
|
70
72
|
"@tanstack/start-fn-stubs": "1.161.6",
|
|
71
|
-
"@tanstack/
|
|
73
|
+
"@tanstack/router-core": "1.167.4",
|
|
74
|
+
"@tanstack/start-storage-context": "1.166.12"
|
|
72
75
|
},
|
|
73
76
|
"devDependencies": {
|
|
77
|
+
"@tanstack/intent": "^0.0.14",
|
|
74
78
|
"vite": "*"
|
|
75
79
|
},
|
|
80
|
+
"bin": {
|
|
81
|
+
"intent": "./bin/intent.js"
|
|
82
|
+
},
|
|
76
83
|
"scripts": {
|
|
77
84
|
"clean": "rimraf ./dist && rimraf ./coverage",
|
|
78
85
|
"test": "pnpm test:eslint && pnpm test:types && pnpm test:build && pnpm test:unit",
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: start-core
|
|
3
|
+
description: >-
|
|
4
|
+
Core overview for TanStack Start: tanstackStart() Vite plugin,
|
|
5
|
+
getRouter() factory, root route document shell (HeadContent,
|
|
6
|
+
Scripts, Outlet), client/server entry points, routeTree.gen.ts,
|
|
7
|
+
tsconfig configuration. Entry point for all Start skills.
|
|
8
|
+
type: core
|
|
9
|
+
library: tanstack-start
|
|
10
|
+
library_version: '1.166.2'
|
|
11
|
+
sources:
|
|
12
|
+
- TanStack/router:docs/start/framework/react/build-from-scratch.md
|
|
13
|
+
- TanStack/router:docs/start/framework/react/quick-start.md
|
|
14
|
+
- TanStack/router:docs/start/framework/react/guide/routing.md
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# TanStack Start Core
|
|
18
|
+
|
|
19
|
+
TanStack Start is a full-stack React framework built on TanStack Router and Vite. It adds SSR, streaming, server functions (type-safe RPCs), middleware, server routes, and universal deployment.
|
|
20
|
+
|
|
21
|
+
> **CRITICAL**: All code in TanStack Start is ISOMORPHIC by default — it runs in BOTH server and client environments. Loaders run on both server AND client. To run code exclusively on the server, use `createServerFn`. This is the #1 AI agent mistake.
|
|
22
|
+
> **CRITICAL**: TanStack Start is NOT Next.js. Do not generate `getServerSideProps`, `"use server"` directives, `app/layout.tsx`, or any Next.js/Remix patterns. Use `createServerFn` for server-only code.
|
|
23
|
+
> **CRITICAL**: Types are FULLY INFERRED. Never cast, never annotate inferred values.
|
|
24
|
+
|
|
25
|
+
## Sub-Skills
|
|
26
|
+
|
|
27
|
+
| Task | Sub-Skill |
|
|
28
|
+
| -------------------------------------------- | ------------------------------------------------------------------- |
|
|
29
|
+
| Type-safe RPCs, data fetching, mutations | [start-core/server-functions/SKILL.md](./server-functions/SKILL.md) |
|
|
30
|
+
| Request/function middleware, context, auth | [start-core/middleware/SKILL.md](./middleware/SKILL.md) |
|
|
31
|
+
| Isomorphic execution, environment boundaries | [start-core/execution-model/SKILL.md](./execution-model/SKILL.md) |
|
|
32
|
+
| REST API endpoints alongside app routes | [start-core/server-routes/SKILL.md](./server-routes/SKILL.md) |
|
|
33
|
+
| Hosting, SSR modes, prerendering, SEO | [start-core/deployment/SKILL.md](./deployment/SKILL.md) |
|
|
34
|
+
|
|
35
|
+
## Quick Decision Tree
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
Need to run code exclusively on the server (DB, secrets)?
|
|
39
|
+
→ start-core/server-functions
|
|
40
|
+
|
|
41
|
+
Need auth checks, logging, or shared logic across server functions?
|
|
42
|
+
→ start-core/middleware
|
|
43
|
+
|
|
44
|
+
Need to understand where code runs (server vs client)?
|
|
45
|
+
→ start-core/execution-model
|
|
46
|
+
|
|
47
|
+
Need a REST API endpoint (GET/POST/PUT/DELETE)?
|
|
48
|
+
→ start-core/server-routes
|
|
49
|
+
|
|
50
|
+
Need to deploy, configure SSR, or prerender?
|
|
51
|
+
→ start-core/deployment
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Project Setup
|
|
55
|
+
|
|
56
|
+
### 1. Install Dependencies
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npm i @tanstack/react-start @tanstack/react-router react react-dom
|
|
60
|
+
npm i -D vite @vitejs/plugin-react typescript
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 2. Configure Vite
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
// vite.config.ts
|
|
67
|
+
import { defineConfig } from 'vite'
|
|
68
|
+
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
|
|
69
|
+
import viteReact from '@vitejs/plugin-react'
|
|
70
|
+
|
|
71
|
+
export default defineConfig({
|
|
72
|
+
plugins: [
|
|
73
|
+
// MUST come before react()
|
|
74
|
+
tanstackStart(),
|
|
75
|
+
viteReact(),
|
|
76
|
+
],
|
|
77
|
+
})
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 3. Create Router Factory
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
// src/router.tsx
|
|
84
|
+
import { createRouter } from '@tanstack/react-router'
|
|
85
|
+
import { routeTree } from './routeTree.gen'
|
|
86
|
+
|
|
87
|
+
export function getRouter() {
|
|
88
|
+
const router = createRouter({
|
|
89
|
+
routeTree,
|
|
90
|
+
scrollRestoration: true,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
return router
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 4. Create Root Route with Document Shell
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
// src/routes/__root.tsx
|
|
101
|
+
import type { ReactNode } from 'react'
|
|
102
|
+
import {
|
|
103
|
+
Outlet,
|
|
104
|
+
createRootRoute,
|
|
105
|
+
HeadContent,
|
|
106
|
+
Scripts,
|
|
107
|
+
} from '@tanstack/react-router'
|
|
108
|
+
|
|
109
|
+
export const Route = createRootRoute({
|
|
110
|
+
head: () => ({
|
|
111
|
+
meta: [
|
|
112
|
+
{ charSet: 'utf-8' },
|
|
113
|
+
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
|
114
|
+
{ title: 'My App' },
|
|
115
|
+
],
|
|
116
|
+
}),
|
|
117
|
+
component: RootComponent,
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
function RootComponent() {
|
|
121
|
+
return (
|
|
122
|
+
<html>
|
|
123
|
+
<head>
|
|
124
|
+
<HeadContent />
|
|
125
|
+
</head>
|
|
126
|
+
<body>
|
|
127
|
+
<Outlet />
|
|
128
|
+
<Scripts />
|
|
129
|
+
</body>
|
|
130
|
+
</html>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 5. Create Index Route with Server Function
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
// src/routes/index.tsx
|
|
139
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
140
|
+
import { createServerFn } from '@tanstack/react-start'
|
|
141
|
+
|
|
142
|
+
const getGreeting = createServerFn({ method: 'GET' }).handler(async () => {
|
|
143
|
+
return { message: 'Hello from the server!' }
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
export const Route = createFileRoute('/')({
|
|
147
|
+
loader: () => getGreeting(),
|
|
148
|
+
component: HomePage,
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
function HomePage() {
|
|
152
|
+
const data = Route.useLoaderData()
|
|
153
|
+
return <h1>{data.message}</h1>
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Common Mistakes
|
|
158
|
+
|
|
159
|
+
### 1. CRITICAL: React plugin before Start plugin in Vite config
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
// WRONG — route generation and server function compilation fail
|
|
163
|
+
plugins: [react(), tanstackStart()]
|
|
164
|
+
|
|
165
|
+
// CORRECT — Start plugin must come first
|
|
166
|
+
plugins: [tanstackStart(), react()]
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### 2. HIGH: Enabling verbatimModuleSyntax in tsconfig
|
|
170
|
+
|
|
171
|
+
`verbatimModuleSyntax` causes server bundles to leak into client bundles. Keep it disabled.
|
|
172
|
+
|
|
173
|
+
### 3. HIGH: Missing Scripts component in root route
|
|
174
|
+
|
|
175
|
+
The `<Scripts />` component must be rendered in the `<body>` of the root route. Without it, client-side JavaScript does not load and hydration fails.
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
// WRONG — no Scripts
|
|
179
|
+
function RootComponent() {
|
|
180
|
+
return (
|
|
181
|
+
<html>
|
|
182
|
+
<head>
|
|
183
|
+
<HeadContent />
|
|
184
|
+
</head>
|
|
185
|
+
<body>
|
|
186
|
+
<Outlet />
|
|
187
|
+
</body>
|
|
188
|
+
</html>
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// CORRECT — Scripts in body
|
|
193
|
+
function RootComponent() {
|
|
194
|
+
return (
|
|
195
|
+
<html>
|
|
196
|
+
<head>
|
|
197
|
+
<HeadContent />
|
|
198
|
+
</head>
|
|
199
|
+
<body>
|
|
200
|
+
<Outlet />
|
|
201
|
+
<Scripts />
|
|
202
|
+
</body>
|
|
203
|
+
</html>
|
|
204
|
+
)
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Version Note
|
|
209
|
+
|
|
210
|
+
This skill targets `@tanstack/react-start` v1.166.2 and `@tanstack/start-client-core` v1.166.2.
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: start-core/deployment
|
|
3
|
+
description: >-
|
|
4
|
+
Deploy to Cloudflare Workers, Netlify, Vercel, Node.js/Docker,
|
|
5
|
+
Bun, Railway. Selective SSR (ssr option per route), SPA mode,
|
|
6
|
+
static prerendering, ISR with Cache-Control headers, SEO and
|
|
7
|
+
head management.
|
|
8
|
+
type: sub-skill
|
|
9
|
+
library: tanstack-start
|
|
10
|
+
library_version: '1.166.2'
|
|
11
|
+
requires:
|
|
12
|
+
- start-core
|
|
13
|
+
sources:
|
|
14
|
+
- TanStack/router:docs/start/framework/react/guide/hosting.md
|
|
15
|
+
- TanStack/router:docs/start/framework/react/guide/selective-ssr.md
|
|
16
|
+
- TanStack/router:docs/start/framework/react/guide/static-prerendering.md
|
|
17
|
+
- TanStack/router:docs/start/framework/react/guide/seo.md
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# Deployment and Rendering
|
|
21
|
+
|
|
22
|
+
TanStack Start deploys to any hosting provider via Vite and Nitro. This skill covers hosting setup, SSR configuration, prerendering, and SEO.
|
|
23
|
+
|
|
24
|
+
## Hosting Providers
|
|
25
|
+
|
|
26
|
+
### Cloudflare Workers
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pnpm add -D @cloudflare/vite-plugin wrangler
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
// vite.config.ts
|
|
34
|
+
import { defineConfig } from 'vite'
|
|
35
|
+
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
|
|
36
|
+
import { cloudflare } from '@cloudflare/vite-plugin'
|
|
37
|
+
import viteReact from '@vitejs/plugin-react'
|
|
38
|
+
|
|
39
|
+
export default defineConfig({
|
|
40
|
+
plugins: [
|
|
41
|
+
cloudflare({ viteEnvironment: { name: 'ssr' } }),
|
|
42
|
+
tanstackStart(),
|
|
43
|
+
viteReact(),
|
|
44
|
+
],
|
|
45
|
+
})
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```jsonc
|
|
49
|
+
// wrangler.jsonc
|
|
50
|
+
{
|
|
51
|
+
"name": "my-app",
|
|
52
|
+
"compatibility_date": "2025-09-02",
|
|
53
|
+
"compatibility_flags": ["nodejs_compat"],
|
|
54
|
+
"main": "@tanstack/react-start/server-entry",
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Deploy: `npx wrangler login && pnpm run deploy`
|
|
59
|
+
|
|
60
|
+
### Netlify
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pnpm add -D @netlify/vite-plugin-tanstack-start
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
// vite.config.ts
|
|
68
|
+
import { defineConfig } from 'vite'
|
|
69
|
+
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
|
|
70
|
+
import netlify from '@netlify/vite-plugin-tanstack-start'
|
|
71
|
+
import viteReact from '@vitejs/plugin-react'
|
|
72
|
+
|
|
73
|
+
export default defineConfig({
|
|
74
|
+
plugins: [tanstackStart(), netlify(), viteReact()],
|
|
75
|
+
})
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Deploy: `npx netlify deploy`
|
|
79
|
+
|
|
80
|
+
### Nitro (Vercel, Railway, Node.js, Docker)
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm install nitro@npm:nitro-nightly@latest
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
// vite.config.ts
|
|
88
|
+
import { defineConfig } from 'vite'
|
|
89
|
+
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
|
|
90
|
+
import { nitro } from 'nitro/vite'
|
|
91
|
+
import viteReact from '@vitejs/plugin-react'
|
|
92
|
+
|
|
93
|
+
export default defineConfig({
|
|
94
|
+
plugins: [tanstackStart(), nitro(), viteReact()],
|
|
95
|
+
})
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Build and start: `npm run build && node .output/server/index.mjs`
|
|
99
|
+
|
|
100
|
+
### Bun
|
|
101
|
+
|
|
102
|
+
Bun deployment requires React 19. For React 18, use Node.js deployment.
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
// vite.config.ts — add bun preset to nitro
|
|
106
|
+
plugins: [tanstackStart(), nitro({ preset: 'bun' }), viteReact()]
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Selective SSR
|
|
110
|
+
|
|
111
|
+
Control SSR per route with the `ssr` property.
|
|
112
|
+
|
|
113
|
+
### `ssr: true` (default)
|
|
114
|
+
|
|
115
|
+
Runs `beforeLoad` and `loader` on server, renders component on server:
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
export const Route = createFileRoute('/posts/$postId')({
|
|
119
|
+
ssr: true, // default
|
|
120
|
+
loader: () => fetchPost(), // runs on server during SSR
|
|
121
|
+
component: PostPage, // rendered on server
|
|
122
|
+
})
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### `ssr: false`
|
|
126
|
+
|
|
127
|
+
Disables server execution of `beforeLoad`/`loader` and server rendering:
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
export const Route = createFileRoute('/dashboard')({
|
|
131
|
+
ssr: false,
|
|
132
|
+
loader: () => fetchDashboard(), // runs on client only
|
|
133
|
+
component: DashboardPage, // rendered on client only
|
|
134
|
+
})
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### `ssr: 'data-only'`
|
|
138
|
+
|
|
139
|
+
Runs `beforeLoad`/`loader` on server but renders component on client only:
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
export const Route = createFileRoute('/canvas')({
|
|
143
|
+
ssr: 'data-only',
|
|
144
|
+
loader: () => fetchCanvasData(), // runs on server
|
|
145
|
+
component: CanvasPage, // rendered on client only
|
|
146
|
+
})
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Functional Form
|
|
150
|
+
|
|
151
|
+
Decide SSR at runtime based on params/search:
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
export const Route = createFileRoute('/docs/$docType/$docId')({
|
|
155
|
+
ssr: ({ params }) => {
|
|
156
|
+
if (params.status === 'success' && params.value.docType === 'sheet') {
|
|
157
|
+
return false
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
})
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### SSR Inheritance
|
|
164
|
+
|
|
165
|
+
Children inherit parent SSR config and can only be MORE restrictive:
|
|
166
|
+
|
|
167
|
+
- `true` → `data-only` or `false` (allowed)
|
|
168
|
+
- `false` → `true` (NOT allowed — parent `false` wins)
|
|
169
|
+
|
|
170
|
+
### Default SSR
|
|
171
|
+
|
|
172
|
+
Change the default for all routes in `src/start.ts`:
|
|
173
|
+
|
|
174
|
+
```tsx
|
|
175
|
+
import { createStart } from '@tanstack/react-start'
|
|
176
|
+
|
|
177
|
+
export const startInstance = createStart(() => ({
|
|
178
|
+
defaultSsr: false,
|
|
179
|
+
}))
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Static Prerendering
|
|
183
|
+
|
|
184
|
+
Generate static HTML at build time:
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
// vite.config.ts
|
|
188
|
+
tanstackStart({
|
|
189
|
+
prerender: {
|
|
190
|
+
enabled: true,
|
|
191
|
+
crawlLinks: true,
|
|
192
|
+
concurrency: 14,
|
|
193
|
+
failOnError: true,
|
|
194
|
+
},
|
|
195
|
+
})
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Static routes are auto-discovered. Dynamic routes (e.g. `/users/$userId`) require `crawlLinks` or explicit `pages` config.
|
|
199
|
+
|
|
200
|
+
## SEO and Head Management
|
|
201
|
+
|
|
202
|
+
### Basic Meta Tags
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
export const Route = createFileRoute('/')({
|
|
206
|
+
head: () => ({
|
|
207
|
+
meta: [
|
|
208
|
+
{ title: 'My App - Home' },
|
|
209
|
+
{ name: 'description', content: 'Welcome to My App' },
|
|
210
|
+
],
|
|
211
|
+
}),
|
|
212
|
+
})
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Dynamic Meta from Loader Data
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
export const Route = createFileRoute('/posts/$postId')({
|
|
219
|
+
loader: async ({ params }) => fetchPost(params.postId),
|
|
220
|
+
head: ({ loaderData }) => ({
|
|
221
|
+
meta: [
|
|
222
|
+
{ title: loaderData.title },
|
|
223
|
+
{ name: 'description', content: loaderData.excerpt },
|
|
224
|
+
{ property: 'og:title', content: loaderData.title },
|
|
225
|
+
{ property: 'og:image', content: loaderData.coverImage },
|
|
226
|
+
],
|
|
227
|
+
}),
|
|
228
|
+
})
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Structured Data (JSON-LD)
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
head: ({ loaderData }) => ({
|
|
235
|
+
scripts: [
|
|
236
|
+
{
|
|
237
|
+
type: 'application/ld+json',
|
|
238
|
+
children: JSON.stringify({
|
|
239
|
+
'@context': 'https://schema.org',
|
|
240
|
+
'@type': 'Article',
|
|
241
|
+
headline: loaderData.title,
|
|
242
|
+
}),
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
})
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Dynamic Sitemap via Server Route
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
// src/routes/sitemap[.]xml.ts
|
|
252
|
+
export const Route = createFileRoute('/sitemap.xml')({
|
|
253
|
+
server: {
|
|
254
|
+
handlers: {
|
|
255
|
+
GET: async () => {
|
|
256
|
+
const posts = await fetchAllPosts()
|
|
257
|
+
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
|
|
258
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
259
|
+
${posts.map((p) => `<url><loc>https://myapp.com/posts/${p.id}</loc></url>`).join('')}
|
|
260
|
+
</urlset>`
|
|
261
|
+
return new Response(sitemap, {
|
|
262
|
+
headers: { 'Content-Type': 'application/xml' },
|
|
263
|
+
})
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
})
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Common Mistakes
|
|
271
|
+
|
|
272
|
+
### 1. HIGH: Missing nodejs_compat flag for Cloudflare Workers
|
|
273
|
+
|
|
274
|
+
```jsonc
|
|
275
|
+
// WRONG — Node.js APIs fail at runtime
|
|
276
|
+
{ "compatibility_flags": [] }
|
|
277
|
+
|
|
278
|
+
// CORRECT
|
|
279
|
+
{ "compatibility_flags": ["nodejs_compat"] }
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### 2. MEDIUM: Bun deployment with React 18
|
|
283
|
+
|
|
284
|
+
Bun-specific deployment only works with React 19. Use Node.js deployment for React 18.
|
|
285
|
+
|
|
286
|
+
### 3. MEDIUM: Child route loosening parent SSR config
|
|
287
|
+
|
|
288
|
+
```tsx
|
|
289
|
+
// Parent sets ssr: false
|
|
290
|
+
// WRONG — child cannot upgrade to ssr: true
|
|
291
|
+
const parentRoute = createFileRoute('/dashboard')({ ssr: false })
|
|
292
|
+
const childRoute = createFileRoute('/dashboard/stats')({
|
|
293
|
+
ssr: true, // IGNORED — parent false wins
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
// CORRECT — children can only be MORE restrictive
|
|
297
|
+
const parentRoute = createFileRoute('/dashboard')({ ssr: 'data-only' })
|
|
298
|
+
const childRoute = createFileRoute('/dashboard/stats')({
|
|
299
|
+
ssr: false, // OK — more restrictive than parent
|
|
300
|
+
})
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Cross-References
|
|
304
|
+
|
|
305
|
+
- [start-core/server-routes](../server-routes/SKILL.md) — API endpoints for sitemaps, robots.txt
|
|
306
|
+
- [start-core/execution-model](../execution-model/SKILL.md) — SSR affects where code runs
|