0
# Server-Side Rendering
1
2
Components and utilities for rendering routes on the server with static location context.
3
4
## Capabilities
5
6
### ServerLocation Component
7
8
Location provider component designed for server-side rendering that provides static location context.
9
10
```javascript { .api }
11
/**
12
* Location provider for server-side rendering with static location context
13
* @param props - Server location configuration
14
*/
15
function ServerLocation(props: {
16
url: string;
17
children: React.ReactNode;
18
}): React.ReactElement;
19
```
20
21
**Props:**
22
- `url` (string, required) - The URL to use as the current location
23
- `children` (React.ReactNode) - Child components that will receive location context
24
25
**Usage Examples:**
26
27
```javascript
28
import React from "react";
29
import { renderToString } from "react-dom/server";
30
import { ServerLocation, Router } from "@reach/router";
31
32
// Basic server-side rendering
33
const renderApp = (url) => {
34
const appHtml = renderToString(
35
<ServerLocation url={url}>
36
<Router>
37
<Home path="/" />
38
<About path="/about" />
39
<Contact path="/contact" />
40
</Router>
41
</ServerLocation>
42
);
43
44
return `
45
<!DOCTYPE html>
46
<html>
47
<head><title>My App</title></head>
48
<body>
49
<div id="root">${appHtml}</div>
50
<script src="/client.js"></script>
51
</body>
52
</html>
53
`;
54
};
55
56
// Express.js integration
57
app.get("*", (req, res) => {
58
const html = renderApp(req.url);
59
res.send(html);
60
});
61
62
// Next.js style server rendering
63
const getServerSideProps = async (context) => {
64
const { req } = context;
65
const url = req.url;
66
67
return {
68
props: {
69
initialUrl: url
70
}
71
};
72
};
73
74
const Page = ({ initialUrl }) => (
75
<ServerLocation url={initialUrl}>
76
<App />
77
</ServerLocation>
78
);
79
80
// URL with query parameters and hash
81
const renderWithQuery = () => {
82
const url = "/search?q=react&category=library#results";
83
84
return renderToString(
85
<ServerLocation url={url}>
86
<Router>
87
<SearchResults path="/search" />
88
</Router>
89
</ServerLocation>
90
);
91
};
92
93
// Server rendering with dynamic routes
94
const renderUserPage = (userId) => {
95
const url = `/users/${userId}`;
96
97
return renderToString(
98
<ServerLocation url={url}>
99
<Router>
100
<UserProfile path="/users/:userId" />
101
</Router>
102
</ServerLocation>
103
);
104
};
105
```
106
107
### Server Location Context
108
109
Understanding the location context provided by ServerLocation for server-side rendering.
110
111
```javascript { .api }
112
/**
113
* ServerLocation provides a static location context
114
*/
115
interface ServerLocationContext {
116
location: {
117
pathname: string;
118
search: string;
119
hash: string;
120
};
121
navigate: () => never; // Throws error - navigation not allowed on server
122
}
123
```
124
125
**Usage Examples:**
126
127
```javascript
128
// Components receive parsed location on server
129
const SearchResults = ({ location }) => {
130
// On server: location = { pathname: "/search", search: "?q=react", hash: "" }
131
const query = new URLSearchParams(location.search).get("q");
132
133
return (
134
<div>
135
<h1>Search Results for: {query}</h1>
136
<p>Server-rendered at {location.pathname}</p>
137
</div>
138
);
139
};
140
141
// Server rendering with hooks
142
const HookComponent = () => {
143
const location = useLocation(); // Works on server with ServerLocation
144
145
return (
146
<div>
147
<p>Current path: {location.pathname}</p>
148
<p>Query string: {location.search}</p>
149
</div>
150
);
151
};
152
153
// Error handling for navigation attempts
154
const SafeNavigationComponent = () => {
155
const navigate = useNavigate();
156
157
const handleClick = () => {
158
if (typeof window !== "undefined") {
159
// Only navigate on client
160
navigate("/new-page");
161
} else {
162
console.log("Navigation attempted on server - ignored");
163
}
164
};
165
166
return <button onClick={handleClick}>Navigate</button>;
167
};
168
169
// Server-side route matching
170
const renderMatchedRoute = (url) => {
171
return renderToString(
172
<ServerLocation url={url}>
173
<Router>
174
<Home path="/" />
175
<User path="/users/:userId" />
176
<NotFound default />
177
</Router>
178
</ServerLocation>
179
);
180
};
181
```
182
183
### Server Rendering Patterns
184
185
Common patterns for implementing server-side rendering with Reach Router.
186
187
**Usage Examples:**
188
189
```javascript
190
// Isomorphic application structure
191
const App = ({ serverUrl }) => {
192
if (typeof window === "undefined") {
193
// Server-side rendering
194
return (
195
<ServerLocation url={serverUrl}>
196
<AppRoutes />
197
</ServerLocation>
198
);
199
} else {
200
// Client-side rendering
201
return (
202
<Router>
203
<AppRoutes />
204
</Router>
205
);
206
}
207
};
208
209
const AppRoutes = () => (
210
<>
211
<Home path="/" />
212
<About path="/about" />
213
<Contact path="/contact" />
214
</>
215
);
216
217
// Server-side data fetching
218
const renderWithData = async (url) => {
219
// Extract route parameters for data fetching
220
const match = matchPath("/users/:userId", url);
221
let userData = null;
222
223
if (match) {
224
userData = await fetchUser(match.params.userId);
225
}
226
227
return renderToString(
228
<ServerLocation url={url}>
229
<DataContext.Provider value={{ userData }}>
230
<Router>
231
<UserProfile path="/users/:userId" />
232
</Router>
233
</DataContext.Provider>
234
</ServerLocation>
235
);
236
};
237
238
// SEO optimization with server rendering
239
const renderSEOPage = (url, metadata) => {
240
const appHtml = renderToString(
241
<ServerLocation url={url}>
242
<Router>
243
<SEOPage path="/seo/*" metadata={metadata} />
244
</Router>
245
</ServerLocation>
246
);
247
248
return `
249
<!DOCTYPE html>
250
<html>
251
<head>
252
<title>${metadata.title}</title>
253
<meta name="description" content="${metadata.description}" />
254
<meta property="og:url" content="${metadata.canonicalUrl}" />
255
</head>
256
<body>
257
<div id="root">${appHtml}</div>
258
</body>
259
</html>
260
`;
261
};
262
263
// Hydration-friendly server rendering
264
const createSSRApp = (url) => {
265
const initialData = gatherInitialData(url);
266
267
const html = renderToString(
268
<ServerLocation url={url}>
269
<DataProvider initialData={initialData}>
270
<Router>
271
<AppRoutes />
272
</Router>
273
</DataProvider>
274
</ServerLocation>
275
);
276
277
return {
278
html,
279
initialData: JSON.stringify(initialData)
280
};
281
};
282
283
// Server-side rendering with error boundaries
284
const SafeServerApp = ({ url }) => (
285
<ServerLocation url={url}>
286
<ErrorBoundary>
287
<Router>
288
<Home path="/" />
289
<About path="/about" />
290
<ErrorPage default />
291
</Router>
292
</ErrorBoundary>
293
</ServerLocation>
294
);
295
296
class ErrorBoundary extends React.Component {
297
constructor(props) {
298
super(props);
299
this.state = { hasError: false };
300
}
301
302
static getDerivedStateFromError(error) {
303
return { hasError: true };
304
}
305
306
render() {
307
if (this.state.hasError) {
308
return <div>Something went wrong during server rendering.</div>;
309
}
310
311
return this.props.children;
312
}
313
}
314
```
315
316
### Client-Side Hydration
317
318
Handling the transition from server-rendered content to client-side routing.
319
320
**Usage Examples:**
321
322
```javascript
323
// Client-side hydration setup
324
import { hydrate } from "react-dom";
325
326
const ClientApp = () => (
327
<Router>
328
<AppRoutes />
329
</Router>
330
);
331
332
// Hydrate the server-rendered content
333
hydrate(<ClientApp />, document.getElementById("root"));
334
335
// Hydration with initial data
336
const hydrateWithData = (initialData) => {
337
const App = () => (
338
<DataProvider initialData={initialData}>
339
<Router>
340
<AppRoutes />
341
</Router>
342
</DataProvider>
343
);
344
345
hydrate(<App />, document.getElementById("root"));
346
};
347
348
// Read initial data from server
349
const initialDataScript = document.getElementById("initial-data");
350
const initialData = JSON.parse(initialDataScript.textContent);
351
hydrateWithData(initialData);
352
353
// Handling hydration mismatches
354
const HydrationSafeComponent = ({ children }) => {
355
const [hasMounted, setHasMounted] = React.useState(false);
356
357
React.useEffect(() => {
358
setHasMounted(true);
359
}, []);
360
361
if (!hasMounted) {
362
// Return server-safe content during hydration
363
return <div>{children}</div>;
364
}
365
366
// Return client-specific content after hydration
367
return <div className="client-hydrated">{children}</div>;
368
};
369
370
// Universal routing component
371
const UniversalApp = ({ initialUrl }) => {
372
const [isServer] = React.useState(typeof window === "undefined");
373
374
if (isServer) {
375
return (
376
<ServerLocation url={initialUrl}>
377
<AppRoutes />
378
</ServerLocation>
379
);
380
}
381
382
return (
383
<Router>
384
<AppRoutes />
385
</Router>
386
);
387
};
388
389
// Progressive enhancement approach
390
const ProgressiveApp = () => {
391
const [isHydrated, setIsHydrated] = React.useState(false);
392
393
React.useEffect(() => {
394
setIsHydrated(true);
395
}, []);
396
397
return (
398
<div>
399
{/* Always render core content */}
400
<header>My App</header>
401
402
{/* Conditionally render interactive features */}
403
{isHydrated ? (
404
<Router>
405
<InteractiveRoutes />
406
</Router>
407
) : (
408
<StaticContent />
409
)}
410
</div>
411
);
412
};
413
```