0
# Server-Side Rendering
1
2
Components and utilities for server-side rendering including static routing, data loading on the server, and hydration support.
3
4
## Capabilities
5
6
### StaticRouter
7
8
Router component for server-side rendering with a fixed location.
9
10
```typescript { .api }
11
/**
12
* Router component for server-side rendering
13
* @param props - StaticRouter configuration options
14
* @returns Router component with static location
15
*/
16
function StaticRouter(props: StaticRouterProps): JSX.Element;
17
18
interface StaticRouterProps {
19
/** Base URL for all routes */
20
basename?: string;
21
/** Child routes and components */
22
children?: React.ReactNode;
23
/** Current location for rendering */
24
location: Partial<Location> | string;
25
}
26
27
interface Location {
28
pathname: string;
29
search: string;
30
hash: string;
31
state: any;
32
key: string;
33
}
34
```
35
36
**Usage Example:**
37
38
```tsx
39
import { StaticRouter } from "react-router-dom";
40
import { renderToString } from "react-dom/server";
41
42
// Server-side rendering
43
export function render(request: Request) {
44
const html = renderToString(
45
<StaticRouter location={request.url}>
46
<App />
47
</StaticRouter>
48
);
49
50
return `
51
<!DOCTYPE html>
52
<html>
53
<head><title>My App</title></head>
54
<body>
55
<div id="root">${html}</div>
56
<script src="/client.js"></script>
57
</body>
58
</html>
59
`;
60
}
61
```
62
63
### StaticRouterProvider
64
65
Provider component for static routers with data loading capabilities.
66
67
```typescript { .api }
68
/**
69
* Provider component for static routers with data support
70
* @param props - StaticRouterProvider configuration
71
* @returns Router provider with static handler context
72
*/
73
function StaticRouterProvider(props: StaticRouterProviderProps): JSX.Element;
74
75
interface StaticRouterProviderProps {
76
/** Router instance from createStaticRouter */
77
router: DataRouter;
78
/** Static handler context with data */
79
context: StaticHandlerContext;
80
/** Whether to hydrate on client */
81
hydrate?: boolean;
82
/** Nonce for CSP */
83
nonce?: string;
84
}
85
86
interface StaticHandlerContext {
87
/** Base URL path */
88
basename: string;
89
/** Current location */
90
location: Location;
91
/** Matched routes */
92
matches: StaticHandlerMatch[];
93
/** Data from route loaders */
94
loaderData: Record<string, any>;
95
/** Data from route actions */
96
actionData: Record<string, any> | null;
97
/** Route errors */
98
errors: Record<string, any> | null;
99
/** HTTP status code */
100
statusCode: number;
101
/** Response headers from loaders */
102
loaderHeaders: Record<string, Headers>;
103
/** Response headers from actions */
104
actionHeaders: Record<string, Headers> | null;
105
/** Deferred data promises */
106
activeDeferreds: Record<string, DeferredData> | null;
107
}
108
```
109
110
**Usage Example:**
111
112
```tsx
113
import {
114
createStaticRouter,
115
createStaticHandler,
116
StaticRouterProvider
117
} from "react-router-dom";
118
119
export async function render(request: Request) {
120
const handler = createStaticHandler(routes);
121
const context = await handler.query(request);
122
123
if (context instanceof Response) {
124
return context; // Handle redirects
125
}
126
127
const router = createStaticRouter(routes, context.location);
128
129
const html = renderToString(
130
<StaticRouterProvider router={router} context={context} />
131
);
132
133
return new Response(html, {
134
status: context.statusCode,
135
headers: { "Content-Type": "text/html" },
136
});
137
}
138
```
139
140
### HydratedRouter
141
142
Specialized router for React Server Components and framework-mode applications that hydrates from server state.
143
144
```typescript { .api }
145
/**
146
* Framework-mode router for hydrating from server state
147
* @param props - HydratedRouter configuration options
148
* @returns Hydrated router component for SSR applications
149
*/
150
function HydratedRouter(props: HydratedRouterProps): JSX.Element;
151
152
interface HydratedRouterProps {
153
/** Context function for clientAction/clientLoader */
154
unstable_getContext?: RouterInit["unstable_getContext"];
155
/** Error handler for application errors */
156
unstable_onError?: (error: any, errorInfo: React.ErrorInfo) => void;
157
}
158
159
interface RouterInit {
160
/** Function to get context for client-side route functions */
161
unstable_getContext?: () => any;
162
}
163
```
164
165
**Usage Example:**
166
167
```tsx
168
import { HydratedRouter } from "react-router-dom";
169
170
// Client-side entry point for framework apps
171
function App() {
172
return (
173
<HydratedRouter
174
unstable_onError={(error, errorInfo) => {
175
console.error("App error:", error, errorInfo);
176
reportError(error);
177
}}
178
/>
179
);
180
}
181
```
182
183
### ServerRouter
184
185
Server-side router component for React Server Components.
186
187
```typescript { .api }
188
/**
189
* Server router component for React Server Components
190
* @param props - ServerRouter configuration
191
* @returns Server-side router for RSC applications
192
*/
193
function ServerRouter(props: ServerRouterProps): JSX.Element;
194
195
interface ServerRouterProps {
196
/** Current request context */
197
context: EntryContext;
198
/** Request URL */
199
url: string;
200
/** Abort signal for request cancellation */
201
abortDelay?: number;
202
/** Nonce for content security policy */
203
nonce?: string;
204
}
205
206
interface EntryContext {
207
/** Server manifest */
208
manifest: AssetsManifest;
209
/** Route modules */
210
routeModules: RouteModules;
211
/** Server build */
212
serverBuild: ServerBuild;
213
/** Static handler context */
214
staticHandlerContext: StaticHandlerContext;
215
}
216
```
217
218
### Document Components
219
220
Components for managing document head and scripts in SSR applications.
221
222
```typescript { .api }
223
/**
224
* Component for rendering document metadata
225
* @param props - Meta component options
226
* @returns Meta tags for document head
227
*/
228
function Meta(props?: { nonce?: string }): JSX.Element;
229
230
/**
231
* Component for rendering document links
232
* @param props - Links component options
233
* @returns Link tags for document head
234
*/
235
function Links(props?: LinksProps): JSX.Element;
236
237
/**
238
* Component for rendering application scripts
239
* @param props - Scripts component options
240
* @returns Script tags for document body
241
*/
242
function Scripts(props?: ScriptsProps): JSX.Element;
243
244
/**
245
* Component for prefetching page links
246
* @param props - PrefetchPageLinks options
247
* @returns Prefetch link tags
248
*/
249
function PrefetchPageLinks(props?: { page?: string }): JSX.Element;
250
251
interface LinksProps {
252
/** Nonce for CSP */
253
nonce?: string;
254
}
255
256
interface ScriptsProps {
257
/** Nonce for CSP */
258
nonce?: string;
259
/** Async script loading */
260
async?: boolean;
261
/** Cross-origin setting */
262
crossOrigin?: string;
263
}
264
```
265
266
**Usage Example:**
267
268
```tsx
269
import { Links, Meta, Scripts } from "react-router-dom";
270
271
function Document({ children, title }) {
272
return (
273
<html lang="en">
274
<head>
275
<meta charSet="utf-8" />
276
<meta name="viewport" content="width=device-width,initial-scale=1" />
277
<title>{title}</title>
278
<Meta />
279
<Links />
280
</head>
281
<body>
282
{children}
283
<Scripts />
284
</body>
285
</html>
286
);
287
}
288
```
289
290
### Route Module Functions
291
292
Server-side functions for generating metadata and handling requests.
293
294
```typescript { .api }
295
/**
296
* Function for generating document metadata
297
*/
298
type MetaFunction<
299
Loader = unknown,
300
ParentsLoaders extends Record<string, unknown> = {}
301
> = (args: MetaArgs<Loader, ParentsLoaders>) => MetaDescriptor[];
302
303
interface MetaArgs<
304
Loader = unknown,
305
ParentsLoaders extends Record<string, unknown> = {}
306
> {
307
/** Route data from loader */
308
data: Loader;
309
/** Route parameters */
310
params: Params;
311
/** Current location */
312
location: Location;
313
/** Parent route data */
314
matches: UIMatch<unknown, unknown>[];
315
/** Error from route (if any) */
316
error?: unknown;
317
}
318
319
type MetaDescriptor =
320
| { title: string }
321
| { name: string; content: string }
322
| { property: string; content: string }
323
| { httpEquiv: string; content: string }
324
| { charset: string }
325
| { [key: string]: string };
326
327
/**
328
* Function for generating document links
329
*/
330
type LinksFunction = () => LinkDescriptor[];
331
332
type LinkDescriptor =
333
| PageLinkDescriptor
334
| HtmlLinkDescriptor;
335
336
interface PageLinkDescriptor {
337
page: string;
338
[key: string]: unknown;
339
}
340
341
interface HtmlLinkDescriptor {
342
rel: string;
343
href?: string;
344
[key: string]: unknown;
345
}
346
347
/**
348
* Function for generating response headers
349
*/
350
type HeadersFunction = (args: HeadersArgs) => Headers | HeadersInit;
351
352
interface HeadersArgs {
353
loaderHeaders: Headers;
354
parentHeaders: Headers;
355
actionHeaders: Headers;
356
errorHeaders: Headers | undefined;
357
}
358
```
359
360
**Usage Examples:**
361
362
```tsx
363
// Route with metadata
364
export const meta: MetaFunction<typeof loader> = ({ data, location }) => {
365
return [
366
{ title: data.post.title },
367
{
368
name: "description",
369
content: data.post.excerpt
370
},
371
{
372
property: "og:title",
373
content: data.post.title,
374
},
375
{
376
property: "og:url",
377
content: `https://example.com${location.pathname}`,
378
},
379
];
380
};
381
382
// Route with links
383
export const links: LinksFunction = () => {
384
return [
385
{ rel: "stylesheet", href: "/styles/post.css" },
386
{ rel: "prefetch", href: "/api/related-posts" },
387
];
388
};
389
390
// Route with headers
391
export const headers: HeadersFunction = ({ loaderHeaders }) => {
392
return {
393
"Cache-Control": "public, max-age=3600",
394
"Vary": "Accept-Encoding",
395
...Object.fromEntries(loaderHeaders.entries()),
396
};
397
};
398
```
399
400
### Client-Side Functions
401
402
Functions that run on the client for enhanced interactivity.
403
404
```typescript { .api }
405
/**
406
* Client-side loader function
407
*/
408
type ClientLoaderFunction<T = any> = (
409
args: ClientLoaderFunctionArgs
410
) => Promise<T> | T;
411
412
interface ClientLoaderFunctionArgs {
413
request: Request;
414
params: Params;
415
serverLoader: () => Promise<any>;
416
}
417
418
/**
419
* Client-side action function
420
*/
421
type ClientActionFunction<T = any> = (
422
args: ClientActionFunctionArgs
423
) => Promise<T> | T;
424
425
interface ClientActionFunctionArgs {
426
request: Request;
427
params: Params;
428
serverAction: () => Promise<any>;
429
}
430
```
431
432
**Usage Examples:**
433
434
```tsx
435
// Client loader that enhances server data
436
export const clientLoader: ClientLoaderFunction = async ({
437
serverLoader,
438
params
439
}) => {
440
const serverData = await serverLoader();
441
442
// Add client-specific data
443
const clientData = {
444
...serverData,
445
clientTimestamp: Date.now(),
446
isOnline: navigator.onLine,
447
};
448
449
return clientData;
450
};
451
452
// Client action with optimistic updates
453
export const clientAction: ClientActionFunction = async ({
454
request,
455
serverAction
456
}) => {
457
const formData = await request.formData();
458
459
// Optimistic update
460
updateUIOptimistically(formData);
461
462
try {
463
return await serverAction();
464
} catch (error) {
465
// Revert optimistic update
466
revertOptimisticUpdate();
467
throw error;
468
}
469
};
470
```
471
472
### Testing Utilities
473
474
Utilities for testing server-side rendered components.
475
476
```typescript { .api }
477
/**
478
* Create route stub for testing
479
* @param routes - Route definitions for testing
480
* @param opts - Test configuration options
481
* @returns Component for testing routes
482
*/
483
function createRoutesStub(
484
routes: RouteObject[],
485
opts?: {
486
basename?: string;
487
initialEntries?: string[];
488
initialIndex?: number;
489
}
490
): React.ComponentType<RoutesTestStubProps>;
491
492
interface RoutesTestStubProps {
493
children?: React.ReactNode;
494
}
495
```
496
497
**Usage Example:**
498
499
```tsx
500
import { createRoutesStub } from "react-router-dom";
501
import { render, screen } from "@testing-library/react";
502
503
const RouteStub = createRoutesStub([
504
{
505
path: "/users/:id",
506
element: <UserProfile />,
507
loader: ({ params }) => ({ id: params.id, name: "John Doe" }),
508
},
509
]);
510
511
test("renders user profile", async () => {
512
render(<RouteStub initialEntries={["/users/123"]} />);
513
514
expect(await screen.findByText("John Doe")).toBeInTheDocument();
515
});
516
```
517
518
## SSR Patterns
519
520
### Basic Server Setup
521
522
```tsx
523
// server.ts
524
import { createStaticHandler, createStaticRouter } from "react-router-dom";
525
526
const handler = createStaticHandler(routes);
527
528
export async function handleRequest(request: Request) {
529
const context = await handler.query(request);
530
531
if (context instanceof Response) {
532
return context;
533
}
534
535
const router = createStaticRouter(routes, context.location);
536
537
const html = renderToString(
538
<StaticRouterProvider router={router} context={context} />
539
);
540
541
return new Response(
542
`<!DOCTYPE html><html><body>${html}</body></html>`,
543
{
544
status: context.statusCode,
545
headers: { "Content-Type": "text/html" },
546
}
547
);
548
}
549
```
550
551
### Data Loading on Server
552
553
```tsx
554
// Route with server data loading
555
export const loader = async ({ request, params }) => {
556
const user = await fetchUser(params.id);
557
const posts = await fetchUserPosts(params.id);
558
559
return json({ user, posts }, {
560
headers: {
561
"Cache-Control": "public, max-age=300",
562
},
563
});
564
};
565
566
// Client hydration
567
const router = createBrowserRouter(routes);
568
569
hydrateRoot(
570
document.getElementById("root"),
571
<RouterProvider router={router} />
572
);
573
```
574
575
### Error Handling in SSR
576
577
```tsx
578
// Error boundary for SSR
579
function RootErrorBoundary() {
580
const error = useRouteError();
581
582
if (isRouteErrorResponse(error)) {
583
return (
584
<div>
585
<h1>{error.status} {error.statusText}</h1>
586
<p>{error.data}</p>
587
</div>
588
);
589
}
590
591
return (
592
<div>
593
<h1>Something went wrong!</h1>
594
<pre>{error.message}</pre>
595
</div>
596
);
597
}
598
```