0
# Server-Side Rendering
1
2
React Navigation provides server-side rendering (SSR) support for web applications through the ServerContainer component and related utilities. This enables proper navigation state management during server rendering.
3
4
## Capabilities
5
6
### ServerContainer Component
7
8
Container component specifically designed for server-side rendering contexts.
9
10
```typescript { .api }
11
/**
12
* Container component for server rendering.
13
* Should only be used on the server with react-dom/server for SSR.
14
*/
15
function ServerContainer(
16
props: {
17
/** Location object to base the initial URL for SSR */
18
location: {
19
/** Current pathname */
20
pathname: string;
21
/** Query string */
22
search?: string;
23
/** Hash fragment */
24
hash?: string;
25
};
26
/** Child elements to render the content */
27
children: React.ReactNode;
28
} & React.RefAttributes<ServerContainerRef>
29
): React.ReactElement;
30
31
interface ServerContainerRef {
32
/** Get current navigation options during SSR */
33
getCurrentOptions(): Record<string, any> | undefined;
34
}
35
```
36
37
**Usage Examples:**
38
39
```typescript
40
import { ServerContainer } from '@react-navigation/native';
41
import { renderToString } from 'react-dom/server';
42
43
// Basic server-side rendering setup
44
function renderApp(req: Request) {
45
const location = {
46
pathname: req.url,
47
search: req.query ? `?${new URLSearchParams(req.query).toString()}` : '',
48
};
49
50
const html = renderToString(
51
<ServerContainer location={location}>
52
{/* Your navigation structure */}
53
<Stack.Navigator>
54
<Stack.Screen name="Home" component={HomeScreen} />
55
<Stack.Screen name="About" component={AboutScreen} />
56
</Stack.Navigator>
57
</ServerContainer>
58
);
59
60
return html;
61
}
62
63
// Express.js server setup
64
app.get('*', (req, res) => {
65
const location = {
66
pathname: req.path,
67
search: req.url.includes('?') ? req.url.split('?')[1] : '',
68
};
69
70
const appHtml = renderToString(
71
<ServerContainer location={location}>
72
<App />
73
</ServerContainer>
74
);
75
76
const html = `
77
<!DOCTYPE html>
78
<html>
79
<head>
80
<title>My App</title>
81
<meta charset="utf-8" />
82
</head>
83
<body>
84
<div id="root">${appHtml}</div>
85
<script src="/client.js"></script>
86
</body>
87
</html>
88
`;
89
90
res.send(html);
91
});
92
93
// Next.js integration
94
function MyApp({ Component, pageProps, router }) {
95
if (typeof window === 'undefined') {
96
// Server-side rendering
97
return (
98
<ServerContainer location={{ pathname: router.asPath }}>
99
<Component {...pageProps} />
100
</ServerContainer>
101
);
102
}
103
104
// Client-side rendering
105
return (
106
<NavigationContainer>
107
<Component {...pageProps} />
108
</NavigationContainer>
109
);
110
}
111
```
112
113
### Server Context
114
115
React context that provides server-side location information to child components.
116
117
```typescript { .api }
118
interface ServerContextType {
119
/** Location object for server rendering */
120
location: {
121
pathname: string;
122
search?: string;
123
hash?: string;
124
};
125
}
126
127
// Server context is automatically provided by ServerContainer
128
// Access it using React.useContext if needed for custom components
129
const ServerContext: React.Context<ServerContextType | undefined>;
130
```
131
132
**Context Usage Examples:**
133
134
```typescript
135
import { ServerContext } from '@react-navigation/native';
136
137
// Access server location in components
138
function ServerAwareComponent() {
139
const serverContext = useContext(ServerContext);
140
141
if (serverContext) {
142
// Running on server
143
const { pathname, search } = serverContext.location;
144
console.log('Server rendering path:', pathname + (search || ''));
145
}
146
147
return <Text>Server-aware content</Text>;
148
}
149
150
// Conditional rendering based on server context
151
function ConditionalServerComponent() {
152
const serverContext = useContext(ServerContext);
153
const isServer = serverContext !== undefined;
154
155
return (
156
<View>
157
{isServer ? (
158
<Text>Rendered on server</Text>
159
) : (
160
<Text>Rendered on client</Text>
161
)}
162
</View>
163
);
164
}
165
```
166
167
### SSR Navigation State
168
169
Handle navigation state during server-side rendering with proper hydration.
170
171
```typescript { .api }
172
// Server navigation state management
173
interface SSRNavigationState {
174
/** Initial state derived from server location */
175
initialState?: NavigationState;
176
/** Whether hydration should preserve server state */
177
preserveState?: boolean;
178
}
179
```
180
181
**State Management Examples:**
182
183
```typescript
184
// Server-side state extraction
185
function getInitialNavigationState(pathname: string): NavigationState | undefined {
186
// Parse pathname to determine initial navigation state
187
if (pathname === '/') {
188
return {
189
routes: [{ name: 'Home', key: 'home' }],
190
index: 0,
191
};
192
} else if (pathname.startsWith('/profile/')) {
193
const userId = pathname.split('/')[2];
194
return {
195
routes: [
196
{ name: 'Home', key: 'home' },
197
{ name: 'Profile', key: 'profile', params: { userId } },
198
],
199
index: 1,
200
};
201
}
202
203
return undefined;
204
}
205
206
// SSR with navigation state
207
function serverRender(req: Request) {
208
const location = { pathname: req.path };
209
const initialState = getInitialNavigationState(req.path);
210
211
const html = renderToString(
212
<ServerContainer location={location}>
213
<NavigationContainer initialState={initialState}>
214
<Stack.Navigator>
215
<Stack.Screen name="Home" component={HomeScreen} />
216
<Stack.Screen name="Profile" component={ProfileScreen} />
217
</Stack.Navigator>
218
</NavigationContainer>
219
</ServerContainer>
220
);
221
222
return html;
223
}
224
225
// Client-side hydration
226
function clientHydrate() {
227
const initialState = window.__INITIAL_NAVIGATION_STATE__;
228
229
hydrateRoot(
230
document.getElementById('root'),
231
<NavigationContainer initialState={initialState}>
232
<App />
233
</NavigationContainer>
234
);
235
}
236
```
237
238
### SEO and Meta Tags
239
240
Handle SEO metadata and document titles during server-side rendering.
241
242
```typescript { .api }
243
// SEO support through server container ref
244
interface SSRMetadata {
245
title?: string;
246
description?: string;
247
canonicalUrl?: string;
248
openGraph?: Record<string, string>;
249
}
250
```
251
252
**SEO Examples:**
253
254
```typescript
255
// Extract metadata during SSR
256
function renderWithMetadata(req: Request) {
257
const serverContainerRef = useRef<ServerContainerRef>(null);
258
const location = { pathname: req.path };
259
260
const html = renderToString(
261
<ServerContainer ref={serverContainerRef} location={location}>
262
<App />
263
</ServerContainer>
264
);
265
266
// Extract navigation options for metadata
267
const options = serverContainerRef.current?.getCurrentOptions();
268
const title = options?.title || 'Default Title';
269
const description = options?.description || 'Default description';
270
271
const fullHtml = `
272
<!DOCTYPE html>
273
<html>
274
<head>
275
<title>${title}</title>
276
<meta name="description" content="${description}" />
277
<meta property="og:title" content="${title}" />
278
<meta property="og:description" content="${description}" />
279
<link rel="canonical" href="${req.protocol}://${req.get('host')}${req.path}" />
280
</head>
281
<body>
282
<div id="root">${html}</div>
283
</body>
284
</html>
285
`;
286
287
return fullHtml;
288
}
289
290
// Screen with SEO metadata
291
function ProfileScreen({ route }) {
292
const { userId } = route.params;
293
294
useLayoutEffect(() => {
295
navigation.setOptions({
296
title: `Profile - ${userId}`,
297
description: `View profile information for user ${userId}`,
298
});
299
}, [userId]);
300
301
return <ProfileContent userId={userId} />;
302
}
303
```
304
305
### Error Handling
306
307
Handle errors gracefully during server-side rendering.
308
309
```typescript { .api }
310
// SSR error handling patterns
311
interface SSRErrorBoundary {
312
/** Fallback component for server errors */
313
fallback: React.ComponentType<{ error: Error }>;
314
/** Error logging during SSR */
315
onError?: (error: Error) => void;
316
}
317
```
318
319
**Error Handling Examples:**
320
321
```typescript
322
// Server error boundary
323
class SSRErrorBoundary extends React.Component {
324
constructor(props) {
325
super(props);
326
this.state = { hasError: false, error: null };
327
}
328
329
static getDerivedStateFromError(error) {
330
return { hasError: true, error };
331
}
332
333
componentDidCatch(error, errorInfo) {
334
// Log error during SSR
335
console.error('SSR Navigation Error:', error, errorInfo);
336
}
337
338
render() {
339
if (this.state.hasError) {
340
return (
341
<div>
342
<h1>Navigation Error</h1>
343
<p>Unable to render navigation on server</p>
344
</div>
345
);
346
}
347
348
return this.props.children;
349
}
350
}
351
352
// Safe SSR rendering
353
function safeServerRender(req: Request) {
354
try {
355
const location = { pathname: req.path };
356
357
const html = renderToString(
358
<SSRErrorBoundary>
359
<ServerContainer location={location}>
360
<App />
361
</ServerContainer>
362
</SSRErrorBoundary>
363
);
364
365
return html;
366
} catch (error) {
367
console.error('Server rendering failed:', error);
368
369
// Return minimal HTML fallback
370
return `
371
<div id="root">
372
<div>Loading...</div>
373
<script>
374
// Client will take over
375
window.__SSR_ERROR__ = true;
376
</script>
377
</div>
378
`;
379
}
380
}
381
382
// Client-side fallback handling
383
function clientWithFallback() {
384
const hasSSRError = window.__SSR_ERROR__;
385
386
if (hasSSRError) {
387
// Full client-side rendering
388
render(
389
<NavigationContainer>
390
<App />
391
</NavigationContainer>,
392
document.getElementById('root')
393
);
394
} else {
395
// Normal hydration
396
hydrateRoot(
397
document.getElementById('root'),
398
<NavigationContainer>
399
<App />
400
</NavigationContainer>
401
);
402
}
403
}
404
```
405
406
### Platform Detection
407
408
Properly detect and handle server-side rendering context.
409
410
```typescript { .api }
411
// Platform detection utilities
412
interface PlatformDetection {
413
isServer: boolean;
414
isClient: boolean;
415
canUseDOM: boolean;
416
}
417
```
418
419
**Platform Detection Examples:**
420
421
```typescript
422
// Universal platform detection
423
const isServer = typeof window === 'undefined';
424
const isClient = typeof window !== 'undefined';
425
426
// Navigation container selection
427
function UniversalNavigationContainer({ children, ...props }) {
428
if (isServer) {
429
return (
430
<ServerContainer {...props}>
431
{children}
432
</ServerContainer>
433
);
434
}
435
436
return (
437
<NavigationContainer {...props}>
438
{children}
439
</NavigationContainer>
440
);
441
}
442
443
// Feature detection during SSR
444
function FeatureAwareComponent() {
445
const [canUseFeature, setCanUseFeature] = useState(false);
446
447
useEffect(() => {
448
// Only run on client
449
if (typeof window !== 'undefined') {
450
setCanUseFeature(true);
451
}
452
}, []);
453
454
return (
455
<View>
456
{canUseFeature ? (
457
<ClientOnlyFeature />
458
) : (
459
<ServerSafeContent />
460
)}
461
</View>
462
);
463
}
464
```