0
# Server-Side Rendering
1
2
Complete SSR support with server and client components, rendering utilities, hydration, stream handling, and asset management for modern web applications.
3
4
## Capabilities
5
6
### Server-Side Rendering Functions
7
8
Functions for rendering router to string or stream on the server.
9
10
```typescript { .api }
11
/**
12
* Render router to string for SSR
13
* @param options - Server rendering options
14
* @returns Promise resolving to rendered HTML string
15
*/
16
function renderRouterToString<TRouter extends AnyRouter>(
17
options: RenderRouterToStringOptions<TRouter>
18
): Promise<string>;
19
20
/**
21
* Render router to stream for SSR with streaming
22
* @param options - Stream rendering options
23
* @returns Promise resolving to readable stream
24
*/
25
function renderRouterToStream<TRouter extends AnyRouter>(
26
options: RenderRouterToStreamOptions<TRouter>
27
): Promise<ReadableStream<Uint8Array>>;
28
29
interface RenderRouterToStringOptions<TRouter extends AnyRouter> {
30
/** Router instance */
31
router: TRouter;
32
/** Request URL */
33
url: string;
34
/** Request headers */
35
headers?: Record<string, string>;
36
/** Render handler function */
37
renderHandler?: RenderHandler;
38
/** Additional context */
39
context?: any;
40
}
41
42
interface RenderRouterToStreamOptions<TRouter extends AnyRouter> {
43
/** Router instance */
44
router: TRouter;
45
/** Request URL */
46
url: string;
47
/** Request headers */
48
headers?: Record<string, string>;
49
/** Stream handler function */
50
streamHandler?: StreamHandler;
51
/** Render handler function */
52
renderHandler?: RenderHandler;
53
/** Additional context */
54
context?: any;
55
}
56
```
57
58
**Usage Examples:**
59
60
```typescript
61
import { renderRouterToString, renderRouterToStream } from "@tanstack/react-router/ssr/server";
62
63
// Express.js server with string rendering
64
app.get("*", async (req, res) => {
65
try {
66
const html = await renderRouterToString({
67
router,
68
url: req.url,
69
headers: req.headers,
70
context: {
71
user: req.user,
72
session: req.session,
73
},
74
});
75
76
res.status(200).send(`
77
<!DOCTYPE html>
78
<html>
79
<head>
80
<meta charset="utf-8">
81
<title>My App</title>
82
</head>
83
<body>
84
<div id="root">${html}</div>
85
<script src="/client.js"></script>
86
</body>
87
</html>
88
`);
89
} catch (error) {
90
res.status(500).send("Server Error");
91
}
92
});
93
94
// Stream rendering for better performance
95
app.get("*", async (req, res) => {
96
try {
97
const stream = await renderRouterToStream({
98
router,
99
url: req.url,
100
headers: req.headers,
101
});
102
103
res.setHeader("Content-Type", "text/html");
104
res.write(`
105
<!DOCTYPE html>
106
<html>
107
<head>
108
<meta charset="utf-8">
109
<title>My App</title>
110
</head>
111
<body>
112
<div id="root">
113
`);
114
115
const reader = stream.getReader();
116
const pump = () => {
117
return reader.read().then(({ done, value }) => {
118
if (done) {
119
res.end(`
120
</div>
121
<script src="/client.js"></script>
122
</body>
123
</html>
124
`);
125
return;
126
}
127
res.write(new TextDecoder().decode(value));
128
return pump();
129
});
130
};
131
132
return pump();
133
} catch (error) {
134
res.status(500).send("Server Error");
135
}
136
});
137
```
138
139
### Server and Client Components
140
141
Specialized components for server-side and client-side routing.
142
143
```typescript { .api }
144
/**
145
* Server-side router component
146
* @param props - Server router props
147
* @returns JSX element for server rendering
148
*/
149
function RouterServer<TRouter extends AnyRouter>(
150
props: RouterServerProps<TRouter>
151
): JSX.Element;
152
153
/**
154
* Client-side router component for hydration
155
* @param props - Client router props
156
* @returns JSX element for client hydration
157
*/
158
function RouterClient<TRouter extends AnyRouter>(
159
props: RouterClientProps<TRouter>
160
): JSX.Element;
161
162
interface RouterServerProps<TRouter extends AnyRouter> {
163
/** Router instance */
164
router: TRouter;
165
/** Server context */
166
context?: any;
167
/** Dehydrated state */
168
dehydratedState?: any;
169
}
170
171
interface RouterClientProps<TRouter extends AnyRouter> {
172
/** Router instance */
173
router: TRouter;
174
/** Hydration state from server */
175
hydrationState?: any;
176
}
177
```
178
179
**Usage Examples:**
180
181
```typescript
182
// Server-side component usage
183
import { RouterServer } from "@tanstack/react-router/ssr/server";
184
185
function ServerApp({ router, context }: { router: Router; context: any }) {
186
return (
187
<RouterServer
188
router={router}
189
context={context}
190
dehydratedState={{
191
user: context.user,
192
timestamp: Date.now(),
193
}}
194
/>
195
);
196
}
197
198
// Client-side hydration
199
import { RouterClient } from "@tanstack/react-router/ssr/client";
200
201
function ClientApp({ router }: { router: Router }) {
202
const hydrationState = window.__ROUTER_HYDRATION_STATE__;
203
204
return (
205
<RouterClient
206
router={router}
207
hydrationState={hydrationState}
208
/>
209
);
210
}
211
```
212
213
### Render and Stream Handlers
214
215
Default handlers for rendering and streaming functionality.
216
217
```typescript { .api }
218
/**
219
* Default render handler for SSR
220
* @param options - Render handler options
221
* @returns Rendered content
222
*/
223
function defaultRenderHandler<TRouter extends AnyRouter>(
224
options: RenderHandlerOptions<TRouter>
225
): React.ReactElement;
226
227
/**
228
* Default stream handler for SSR streaming
229
* @param options - Stream handler options
230
* @returns Stream configuration
231
*/
232
function defaultStreamHandler<TRouter extends AnyRouter>(
233
options: StreamHandlerOptions<TRouter>
234
): StreamHandlerResult;
235
236
interface RenderHandlerOptions<TRouter extends AnyRouter> {
237
router: TRouter;
238
context?: any;
239
dehydratedState?: any;
240
}
241
242
interface StreamHandlerOptions<TRouter extends AnyRouter> {
243
router: TRouter;
244
context?: any;
245
}
246
247
interface StreamHandlerResult {
248
/** Bootstrap script */
249
bootstrapScript?: string;
250
/** Bootstrap modules */
251
bootstrapModules?: string[];
252
/** Progressive enhancement */
253
progressivelyEnhance?: boolean;
254
}
255
256
type RenderHandler = <TRouter extends AnyRouter>(
257
options: RenderHandlerOptions<TRouter>
258
) => React.ReactElement;
259
260
type StreamHandler = <TRouter extends AnyRouter>(
261
options: StreamHandlerOptions<TRouter>
262
) => StreamHandlerResult;
263
```
264
265
**Usage Examples:**
266
267
```typescript
268
import { defaultRenderHandler, defaultStreamHandler } from "@tanstack/react-router/ssr/server";
269
270
// Custom render handler
271
const customRenderHandler: RenderHandler = ({ router, context, dehydratedState }) => {
272
return (
273
<html>
274
<head>
275
<title>{context.title}</title>
276
<meta name="description" content={context.description} />
277
<HeadContent />
278
</head>
279
<body>
280
<RouterServer
281
router={router}
282
context={context}
283
dehydratedState={dehydratedState}
284
/>
285
<Scripts />
286
<script
287
dangerouslySetInnerHTML={{
288
__html: `window.__ROUTER_HYDRATION_STATE__ = ${JSON.stringify(dehydratedState)};`,
289
}}
290
/>
291
</body>
292
</html>
293
);
294
};
295
296
// Custom stream handler
297
const customStreamHandler: StreamHandler = ({ router, context }) => {
298
return {
299
bootstrapScript: "/static/js/client.js",
300
bootstrapModules: ["/static/js/hydration.js"],
301
progressivelyEnhance: true,
302
};
303
};
304
305
// Use custom handlers
306
const html = await renderRouterToString({
307
router,
308
url: req.url,
309
renderHandler: customRenderHandler,
310
context: {
311
title: "My App",
312
description: "A great application",
313
user: req.user,
314
},
315
});
316
```
317
318
### Asset Management Components
319
320
Components for managing HTML assets, scripts, and metadata in SSR.
321
322
```typescript { .api }
323
/**
324
* Renders route-specific and manifest scripts
325
* @returns JSX element with script tags
326
*/
327
function Scripts(): JSX.Element;
328
329
/**
330
* Renders various HTML assets (scripts, styles, meta, etc.)
331
* @param props - Asset configuration
332
* @returns JSX element with asset tags
333
*/
334
function Asset(props: AssetProps): JSX.Element;
335
336
/**
337
* Renders head content from route matches
338
* @returns JSX element with head content
339
*/
340
function HeadContent(): JSX.Element;
341
342
/**
343
* Ensures scripts are only rendered once
344
* @param props - Script attributes
345
* @returns JSX script element
346
*/
347
function ScriptOnce(props: React.ScriptHTMLAttributes<HTMLScriptElement>): JSX.Element;
348
349
interface AssetProps extends RouterManagedTag {
350
/** Content Security Policy nonce */
351
nonce?: string;
352
}
353
354
interface RouterManagedTag {
355
/** HTML tag type */
356
tag: "script" | "style" | "link" | "meta" | "title";
357
/** Tag attributes */
358
attrs?: Record<string, string>;
359
/** Tag content */
360
children?: string;
361
}
362
```
363
364
**Usage Examples:**
365
366
```typescript
367
import { Scripts, Asset, HeadContent, ScriptOnce } from "@tanstack/react-router";
368
369
// Complete HTML document with assets
370
function DocumentShell() {
371
return (
372
<html>
373
<head>
374
<HeadContent />
375
<Asset
376
tag="meta"
377
attrs={{ charset: "utf-8" }}
378
/>
379
<Asset
380
tag="link"
381
attrs={{
382
rel: "stylesheet",
383
href: "/static/css/app.css",
384
}}
385
/>
386
<ScriptOnce
387
src="/static/js/polyfills.js"
388
defer
389
/>
390
</head>
391
<body>
392
<div id="root">
393
<RouterServer router={router} />
394
</div>
395
<Scripts />
396
</body>
397
</html>
398
);
399
}
400
401
// Conditional asset loading
402
function ConditionalAssets({ isDevelopment }: { isDevelopment: boolean }) {
403
return (
404
<>
405
{isDevelopment && (
406
<ScriptOnce src="/static/js/devtools.js" />
407
)}
408
<Asset
409
tag="link"
410
attrs={{
411
rel: "preconnect",
412
href: "https://api.example.com",
413
}}
414
/>
415
</>
416
);
417
}
418
```
419
420
### Router Context for SSR
421
422
Utilities for accessing router context in SSR environments.
423
424
```typescript { .api }
425
/**
426
* Get router context for SSR usage
427
* @returns React context for router
428
*/
429
function getRouterContext(): React.Context<AnyRouter | undefined>;
430
```
431
432
**Usage Examples:**
433
434
```typescript
435
import { getRouterContext } from "@tanstack/react-router";
436
437
// Access router in SSR context
438
function ServerOnlyComponent() {
439
const RouterContext = getRouterContext();
440
441
return (
442
<RouterContext.Consumer>
443
{(router) => {
444
if (!router) return null;
445
446
return (
447
<div>
448
<p>Current URL: {router.state.location.pathname}</p>
449
<p>Matches: {router.state.matches.length}</p>
450
</div>
451
);
452
}}
453
</RouterContext.Consumer>
454
);
455
}
456
457
// Use with useContext
458
function useRouterSSR() {
459
const RouterContext = getRouterContext();
460
return useContext(RouterContext);
461
}
462
```
463
464
### Location Rewriting for SSR
465
466
Utilities for rewriting URLs and handling base paths in SSR.
467
468
```typescript { .api }
469
/**
470
* Create a basepath rewrite function
471
* @param basepath - Base path to rewrite
472
* @returns Location rewrite function
473
*/
474
function rewriteBasepath(basepath: string): LocationRewrite;
475
476
/**
477
* Compose multiple location rewrite functions
478
* @param rewrites - Array of rewrite functions
479
* @returns Composed rewrite function
480
*/
481
function composeRewrites(...rewrites: LocationRewrite[]): LocationRewrite;
482
483
type LocationRewrite = (location: ParsedLocation) => ParsedLocation;
484
type LocationRewriteFunction = LocationRewrite;
485
```
486
487
**Usage Examples:**
488
489
```typescript
490
import { rewriteBasepath, composeRewrites } from "@tanstack/react-router";
491
492
// Basepath rewriting
493
const basepathRewrite = rewriteBasepath("/app");
494
495
// Custom location rewrite
496
const customRewrite: LocationRewrite = (location) => ({
497
...location,
498
pathname: location.pathname.replace(/^\/old/, "/new"),
499
});
500
501
// Compose multiple rewrites
502
const composedRewrite = composeRewrites(
503
basepathRewrite,
504
customRewrite,
505
(location) => ({
506
...location,
507
search: { ...location.search, timestamp: Date.now() },
508
})
509
);
510
511
// Use in router configuration
512
const router = createRouter({
513
routeTree,
514
basepath: "/app",
515
// Apply location rewrites
516
rewrite: composedRewrite,
517
});
518
```
519
520
### Serialization for SSR
521
522
Serialization utilities for transferring data between server and client.
523
524
```typescript { .api }
525
/**
526
* Create a serialization adapter
527
* @param adapter - Serialization configuration
528
* @returns Serialization adapter
529
*/
530
function createSerializationAdapter<T>(
531
adapter: SerializationAdapter<T>
532
): SerializationAdapter<T>;
533
534
interface SerializationAdapter<T = any> {
535
/** Serialize value for transport */
536
serialize: (value: T) => string;
537
/** Deserialize value from transport */
538
deserialize: (value: string) => T;
539
}
540
541
type AnySerializationAdapter = SerializationAdapter<any>;
542
```
543
544
**Usage Examples:**
545
546
```typescript
547
import { createSerializationAdapter } from "@tanstack/react-router";
548
549
// Custom date serialization
550
const dateAdapter = createSerializationAdapter({
551
serialize: (date: Date) => date.toISOString(),
552
deserialize: (dateString: string) => new Date(dateString),
553
});
554
555
// Complex object serialization
556
const complexAdapter = createSerializationAdapter({
557
serialize: (obj) => {
558
return JSON.stringify(obj, (key, value) => {
559
if (value instanceof Date) return { __type: "Date", value: value.toISOString() };
560
if (value instanceof Map) return { __type: "Map", value: Array.from(value.entries()) };
561
return value;
562
});
563
},
564
deserialize: (str) => {
565
return JSON.parse(str, (key, value) => {
566
if (value?.__type === "Date") return new Date(value.value);
567
if (value?.__type === "Map") return new Map(value.value);
568
return value;
569
});
570
},
571
});
572
573
// Use in router
574
const router = createRouter({
575
routeTree,
576
serializationAdapter: complexAdapter,
577
});
578
```
579
580
## Types
581
582
### SSR Configuration Types
583
584
```typescript { .api }
585
interface SSROptions {
586
/** Enable server-side rendering */
587
ssr?: boolean;
588
/** Hydration strategy */
589
hydrationStrategy?: "progressive" | "immediate" | "lazy";
590
/** Stream rendering options */
591
streaming?: boolean;
592
/** Asset preloading strategy */
593
assetPreloading?: "aggressive" | "conservative" | "none";
594
}
595
596
interface HydrationState {
597
/** Router state for hydration */
598
routerState: RouterState;
599
/** Dehydrated loader data */
600
loaderData: Record<string, any>;
601
/** Timestamp of server render */
602
timestamp: number;
603
}
604
```
605
606
### Stream Types
607
608
```typescript { .api }
609
interface StreamOptions {
610
/** Bootstrap scripts */
611
bootstrapScripts?: string[];
612
/** Bootstrap modules */
613
bootstrapModules?: string[];
614
/** Progressive enhancement */
615
progressivelyEnhance?: boolean;
616
/** Identifier for the stream */
617
identifierPrefix?: string;
618
}
619
```