In TanStack Start werden Routen, die der anfänglichen Anfrage entsprechen, standardmäßig auf dem Server gerendert. Das bedeutet, dass beforeLoad und loader auf dem Server ausgeführt werden, gefolgt vom Rendern der Routenkomponenten. Der resultierende HTML wird an den Client gesendet, der das Markup zu einer voll interaktiven Anwendung hydriert.
Es gibt jedoch Fälle, in denen Sie SSR für bestimmte Routen oder alle Routen deaktivieren möchten, z. B.
Die selektive SSR-Funktion von TanStack Start ermöglicht es Ihnen, zu konfigurieren
Der SPA-Modus von TanStack Start deaktiviert die serverseitige Ausführung von beforeLoad und loader sowie das serverseitige Rendern von Routenkomponenten vollständig. Selektives SSR ermöglicht es Ihnen, die serverseitige Verarbeitung pro Route zu konfigurieren, entweder statisch oder dynamisch.
Sie können steuern, wie eine Route während der anfänglichen Serveranfrage behandelt wird, indem Sie die Eigenschaft ssr verwenden. Wenn diese Eigenschaft nicht gesetzt ist, ist ihr Standardwert true. Sie können diesen Standardwert mit der Option defaultSsr in createRouter ändern.
// src/router.tsx
import { createRouter as createTanStackRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
export function createRouter() {
const router = createTanStackRouter({
routeTree,
scrollRestoration: true,
defaultPendingComponent: () => <div>Loading...</div>,
// Disable SSR by default
defaultSsr: false,
})
return router
}
// src/router.tsx
import { createRouter as createTanStackRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
export function createRouter() {
const router = createTanStackRouter({
routeTree,
scrollRestoration: true,
defaultPendingComponent: () => <div>Loading...</div>,
// Disable SSR by default
defaultSsr: false,
})
return router
}
Dies ist das Standardverhalten, sofern nicht anders konfiguriert. Bei der anfänglichen Anfrage wird Folgendes geschehen:
// src/routes/posts/$postId.tsx
export const Route = createFileRoute('/posts/$postId')({
ssr: true,
beforeLoad: () => {
console.log('Executes on the server during the initial request')
console.log('Executes on the client for subsequent navigation')
},
loader: () => {
console.log('Executes on the server during the initial request')
console.log('Executes on the client for subsequent navigation')
},
component: () => <div>This component is rendered on the server</div>,
})
// src/routes/posts/$postId.tsx
export const Route = createFileRoute('/posts/$postId')({
ssr: true,
beforeLoad: () => {
console.log('Executes on the server during the initial request')
console.log('Executes on the client for subsequent navigation')
},
loader: () => {
console.log('Executes on the server during the initial request')
console.log('Executes on the client for subsequent navigation')
},
component: () => <div>This component is rendered on the server</div>,
})
Dies deaktiviert serverseitige
// src/routes/posts/$postId.tsx
export const Route = createFileRoute('/posts/$postId')({
ssr: false,
beforeLoad: () => {
console.log('Executes on the client during hydration')
},
loader: () => {
console.log('Executes on the client during hydration')
},
component: () => <div>This component is rendered on the client</div>,
})
// src/routes/posts/$postId.tsx
export const Route = createFileRoute('/posts/$postId')({
ssr: false,
beforeLoad: () => {
console.log('Executes on the client during hydration')
},
loader: () => {
console.log('Executes on the client during hydration')
},
component: () => <div>This component is rendered on the client</div>,
})
Diese hybride Option wird
// src/routes/posts/$postId.tsx
export const Route = createFileRoute('/posts/$postId')({
ssr: 'data-only',
beforeLoad: () => {
console.log('Executes on the server during the initial request')
console.log('Executes on the client for subsequent navigation')
},
loader: () => {
console.log('Executes on the server during the initial request')
console.log('Executes on the client for subsequent navigation')
},
component: () => <div>This component is rendered on the client</div>,
})
// src/routes/posts/$postId.tsx
export const Route = createFileRoute('/posts/$postId')({
ssr: 'data-only',
beforeLoad: () => {
console.log('Executes on the server during the initial request')
console.log('Executes on the client for subsequent navigation')
},
loader: () => {
console.log('Executes on the server during the initial request')
console.log('Executes on the client for subsequent navigation')
},
component: () => <div>This component is rendered on the client</div>,
})
Für mehr Flexibilität können Sie die funktionale Form der ssr-Eigenschaft verwenden, um zur Laufzeit zu entscheiden, ob eine Route SSR-gerendert werden soll.
// src/routes/docs/$docType/$docId.tsx
export const Route = createFileRoute('/docs/$docType/$docId')({
validateSearch: z.object({ details: z.boolean().optional() }),
ssr: ({ params, search }) => {
if (params.status === 'success' && params.value.docType === 'sheet') {
return false
}
if (search.status === 'success' && search.value.details) {
return 'data-only'
}
},
beforeLoad: () => {
console.log('Executes on the server depending on the result of ssr()')
},
loader: () => {
console.log('Executes on the server depending on the result of ssr()')
},
component: () => <div>This component is rendered on the client</div>,
})
// src/routes/docs/$docType/$docId.tsx
export const Route = createFileRoute('/docs/$docType/$docId')({
validateSearch: z.object({ details: z.boolean().optional() }),
ssr: ({ params, search }) => {
if (params.status === 'success' && params.value.docType === 'sheet') {
return false
}
if (search.status === 'success' && search.value.details) {
return 'data-only'
}
},
beforeLoad: () => {
console.log('Executes on the server depending on the result of ssr()')
},
loader: () => {
console.log('Executes on the server depending on the result of ssr()')
},
component: () => <div>This component is rendered on the client</div>,
})
Die ssr-Funktion wird nur auf dem Server während der anfänglichen Anfrage ausgeführt und aus dem Client-Bundle entfernt.
search und params werden nach der Validierung als diskriminierte Union übergeben.
params:
| { status: 'success'; value: Expand<ResolveAllParamsFromParent<TParentRoute, TParams>> }
| { status: 'error'; error: unknown }
search:
| { status: 'success'; value: Expand<ResolveFullSearchSchema<TParentRoute, TSearchValidator>> }
| { status: 'error'; error: unknown }
params:
| { status: 'success'; value: Expand<ResolveAllParamsFromParent<TParentRoute, TParams>> }
| { status: 'error'; error: unknown }
search:
| { status: 'success'; value: Expand<ResolveFullSearchSchema<TParentRoute, TSearchValidator>> }
| { status: 'error'; error: unknown }
Wenn die Validierung fehlschlägt, ist status error und error enthält die Fehlerdetails. Andernfalls ist status success und value enthält die validierten Daten.
Zur Laufzeit erbt eine untergeordnete Route die Konfiguration des selektiven SSR ihrer Elternroute. Der geerbte Wert kann jedoch nur restriktiver geändert werden (d. h. von true zu data-only oder von data-only zu false). Zum Beispiel:
root { ssr: undefined }
posts { ssr: false }
$postId { ssr: true }
root { ssr: undefined }
posts { ssr: false }
$postId { ssr: true }
Ein weiteres Beispiel
root { ssr: undefined }
posts { ssr: 'data-only' }
$postId { ssr: true }
details { ssr: false }
root { ssr: undefined }
posts { ssr: 'data-only' }
$postId { ssr: true }
details { ssr: false }
Für die erste Route mit ssr: false oder ssr: 'data-only' rendert der Server die pendingComponent der Route als Fallback. Wenn pendingComponent nicht konfiguriert ist, wird die defaultPendingComponent gerendert. Wenn beides nicht konfiguriert ist, wird kein Fallback gerendert.
Auf dem Client während der Hydrierung wird dieser Fallback für mindestens minPendingMs (oder defaultPendingMinMs, wenn nicht konfiguriert) angezeigt, auch wenn die Route keine beforeLoad oder loader definiert hat.
Sie können das serverseitige Rendering der Root-Routenkomponente deaktivieren, jedoch muss die <html>-Shell weiterhin auf dem Server gerendert werden. Diese Shell wird über die Eigenschaft shellComponent konfiguriert und nimmt eine einzelne Eigenschaft children entgegen. Die shellComponent wird immer SSR-gerendert und umschließt die Root- component, die Root- errorComponent oder die Root- notFound Komponente entsprechend.
Ein minimales Setup einer Root-Route mit deaktiviertem SSR für die Routenkomponente sieht wie folgt aus:
import * as React from 'react'
import {
HeadContent,
Outlet,
Scripts,
createRootRoute,
} from '@tanstack/react-router'
export const Route = createRootRoute({
shellComponent: RootShell,
component: RootComponent,
errorComponent: () => <div>Error</div>,
notFoundComponent: () => <div>Not found</>,
ssr: false // or `defaultSsr: false` on the router
})
function RootShell({ children }: { children: React.ReactNode }) {
return (
<html>
<head>
<HeadContent />
</head>
<body>
{children}
<Scripts />
</body>
</html>
)
}
function RootComponent() {
return (
<div>
<h1>This component will be rendered on the client</h1>
<Outlet />
</div>
)
}
import * as React from 'react'
import {
HeadContent,
Outlet,
Scripts,
createRootRoute,
} from '@tanstack/react-router'
export const Route = createRootRoute({
shellComponent: RootShell,
component: RootComponent,
errorComponent: () => <div>Error</div>,
notFoundComponent: () => <div>Not found</>,
ssr: false // or `defaultSsr: false` on the router
})
function RootShell({ children }: { children: React.ReactNode }) {
return (
<html>
<head>
<HeadContent />
</head>
<body>
{children}
<Scripts />
</body>
</html>
)
}
function RootComponent() {
return (
<div>
<h1>This component will be rendered on the client</h1>
<Outlet />
</div>
)
}
Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.