0
# Server-Side Rendering Support
1
2
Components and utilities for server-side localization with client hydration support, enabling seamless internationalization in SSR applications.
3
4
## Capabilities
5
6
### PackageLocalizationProvider Component
7
8
Server-only component that injects localized strings into the initial HTML for client-side hydration.
9
10
```typescript { .api }
11
/**
12
* A PackageLocalizationProvider can be rendered on the server to inject the localized strings
13
* needed by the client into the initial HTML.
14
* @param props - Configuration for package localization
15
* @returns JSX.Element for server-side rendering or null on client
16
*/
17
function PackageLocalizationProvider(props: PackageLocalizationProviderProps): JSX.Element | null;
18
19
interface PackageLocalizationProviderProps {
20
/** The target locale for the injected strings */
21
locale: string;
22
/** Package localization strings organized by package name */
23
strings: PackageLocalizedStrings;
24
/** Optional CSP nonce for inline script security */
25
nonce?: string;
26
}
27
28
type PackageLocalizedStrings = {
29
[packageName: string]: Record<string, LocalizedString>;
30
};
31
```
32
33
**Usage Examples:**
34
35
```typescript
36
// Server-side usage (Next.js, Express, etc.)
37
import { PackageLocalizationProvider } from "@react-aria/i18n/server";
38
39
const localizationStrings = {
40
"@myapp/components": {
41
"welcome": "Welcome to our application",
42
"login": "Log in",
43
"logout": "Log out"
44
},
45
"@myapp/forms": {
46
"required": "This field is required",
47
"invalid_email": "Please enter a valid email"
48
}
49
};
50
51
function ServerApp({ locale, nonce }: { locale: string; nonce?: string }) {
52
return (
53
<html>
54
<head>
55
<PackageLocalizationProvider
56
locale={locale}
57
strings={localizationStrings}
58
nonce={nonce}
59
/>
60
</head>
61
<body>
62
<div id="root">
63
{/* Your app content */}
64
</div>
65
</body>
66
</html>
67
);
68
}
69
70
// Next.js integration example
71
import { GetServerSideProps } from "next";
72
73
export const getServerSideProps: GetServerSideProps = async (context) => {
74
const locale = context.locale || "en-US";
75
76
// Load localization strings for the specific locale
77
const strings = await loadLocalizationStrings(locale);
78
79
return {
80
props: {
81
locale,
82
strings
83
}
84
};
85
};
86
87
function MyPage({ locale, strings }: { locale: string; strings: PackageLocalizedStrings }) {
88
return (
89
<>
90
<Head>
91
<PackageLocalizationProvider
92
locale={locale}
93
strings={strings}
94
/>
95
</Head>
96
<main>
97
{/* Your page content */}
98
</main>
99
</>
100
);
101
}
102
103
// Express.js integration example
104
import express from "express";
105
import { renderToString } from "react-dom/server";
106
107
app.get("*", (req, res) => {
108
const locale = req.headers["accept-language"]?.split(",")[0] || "en-US";
109
const strings = getLocalizationStrings(locale);
110
111
const html = renderToString(
112
<PackageLocalizationProvider
113
locale={locale}
114
strings={strings}
115
/>
116
);
117
118
res.send(`
119
<!DOCTYPE html>
120
<html>
121
<head>${html}</head>
122
<body>
123
<div id="root">${/* app content */}</div>
124
</body>
125
</html>
126
`);
127
});
128
```
129
130
### getPackageLocalizationScript Function
131
132
Utility function that generates the script content for injecting localized strings into HTML.
133
134
```typescript { .api }
135
/**
136
* Returns the content for an inline <script> tag to inject localized strings into initial HTML.
137
* @param locale - The target locale string
138
* @param strings - Package localization strings to inject
139
* @returns String content for inline script tag
140
*/
141
function getPackageLocalizationScript(locale: string, strings: PackageLocalizedStrings): string;
142
```
143
144
**Usage Examples:**
145
146
```typescript
147
import { getPackageLocalizationScript } from "@react-aria/i18n/server";
148
149
// Manual script injection
150
function manualScriptInjection() {
151
const locale = "es-ES";
152
const strings = {
153
"@myapp/ui": {
154
"save": "Guardar",
155
"cancel": "Cancelar",
156
"delete": "Eliminar"
157
}
158
};
159
160
const scriptContent = getPackageLocalizationScript(locale, strings);
161
162
// Use with template engines
163
const html = `
164
<!DOCTYPE html>
165
<html>
166
<head>
167
<script>${scriptContent}</script>
168
</head>
169
<body>
170
<!-- App content -->
171
</body>
172
</html>
173
`;
174
175
return html;
176
}
177
178
// Custom server rendering with security headers
179
function secureScriptInjection(nonce: string) {
180
const locale = "fr-FR";
181
const strings = {
182
"@myapp/dashboard": {
183
"overview": "Aperçu",
184
"settings": "Paramètres",
185
"profile": "Profil"
186
}
187
};
188
189
const scriptContent = getPackageLocalizationScript(locale, strings);
190
191
return `<script nonce="${nonce}">${scriptContent}</script>`;
192
}
193
194
// Integration with build-time optimization
195
function buildTimeOptimization() {
196
const supportedLocales = ["en-US", "es-ES", "fr-FR", "de-DE"];
197
const allStrings = loadAllLocalizationStrings();
198
199
// Pre-generate scripts for each locale
200
const precompiledScripts = supportedLocales.reduce((acc, locale) => {
201
const localeStrings = filterStringsByLocale(allStrings, locale);
202
acc[locale] = getPackageLocalizationScript(locale, localeStrings);
203
return acc;
204
}, {} as Record<string, string>);
205
206
// Save to build artifacts for runtime use
207
return precompiledScripts;
208
}
209
```
210
211
### Server-Side Locale Detection
212
213
Utilities for detecting and handling locales in server environments.
214
215
**Common Patterns:**
216
217
```typescript
218
// Express.js locale detection
219
function detectLocaleFromRequest(req: express.Request): string {
220
// Priority: URL param > Accept-Language header > default
221
const urlLocale = req.query.locale as string;
222
const headerLocale = req.headers["accept-language"]?.split(",")[0];
223
224
return urlLocale || headerLocale || "en-US";
225
}
226
227
// Next.js automatic locale detection
228
export const getServerSideProps: GetServerSideProps = async ({ locale, req }) => {
229
// Next.js automatically detects locale from routing or headers
230
const detectedLocale = locale || "en-US";
231
232
const strings = await loadStringsForLocale(detectedLocale);
233
234
return {
235
props: {
236
locale: detectedLocale,
237
strings
238
}
239
};
240
};
241
242
// Custom locale validation and fallback
243
function validateAndFallbackLocale(requestedLocale: string): string {
244
const supportedLocales = ["en-US", "es-ES", "fr-FR", "de-DE"];
245
const normalizedLocale = requestedLocale.toLowerCase();
246
247
// Exact match
248
if (supportedLocales.includes(requestedLocale)) {
249
return requestedLocale;
250
}
251
252
// Language-only fallback (e.g., "es" -> "es-ES")
253
const languageOnly = requestedLocale.split("-")[0];
254
const languageMatch = supportedLocales.find(locale =>
255
locale.startsWith(languageOnly)
256
);
257
258
return languageMatch || "en-US";
259
}
260
```
261
262
### Hydration Considerations
263
264
Important considerations for client-side hydration with server-injected localization.
265
266
**Best Practices:**
267
268
```typescript
269
// Client-side hydration handling
270
import { I18nProvider, useLocalizedStringFormatter } from "@react-aria/i18n";
271
272
function ClientApp({ serverLocale }: { serverLocale: string }) {
273
// Use server locale initially to prevent hydration mismatches
274
const [locale, setLocale] = useState(serverLocale);
275
276
useEffect(() => {
277
// After hydration, can update to browser preference if desired
278
const browserLocale = navigator.language;
279
if (browserLocale !== serverLocale) {
280
// Optionally update locale based on user preference
281
setLocale(browserLocale);
282
}
283
}, [serverLocale]);
284
285
return (
286
<I18nProvider locale={locale}>
287
<App />
288
</I18nProvider>
289
);
290
}
291
292
// Preventing hydration warnings with SSR-safe components
293
function SSRSafeLocaleDisplay() {
294
const [isClient, setIsClient] = useState(false);
295
const { locale } = useLocale();
296
297
useEffect(() => {
298
setIsClient(true);
299
}, []);
300
301
if (!isClient) {
302
// Return server-safe content
303
return <span>Loading locale...</span>;
304
}
305
306
return <span>Current locale: {locale}</span>;
307
}
308
309
// Error boundary for localization failures
310
class LocalizationErrorBoundary extends React.Component {
311
constructor(props) {
312
super(props);
313
this.state = { hasError: false };
314
}
315
316
static getDerivedStateFromError(error) {
317
return { hasError: true };
318
}
319
320
componentDidCatch(error, errorInfo) {
321
console.error("Localization error:", error, errorInfo);
322
}
323
324
render() {
325
if (this.state.hasError) {
326
return <div>Localization failed. Falling back to default content.</div>;
327
}
328
329
return this.props.children;
330
}
331
}
332
```
333
334
### Performance Optimization
335
336
Techniques for optimizing server-side localization performance.
337
338
```typescript
339
// String compression and caching
340
const localizationCache = new Map<string, PackageLocalizedStrings>();
341
342
function getCachedStrings(locale: string): PackageLocalizedStrings {
343
if (!localizationCache.has(locale)) {
344
const strings = loadLocalizationStrings(locale);
345
localizationCache.set(locale, strings);
346
}
347
348
return localizationCache.get(locale)!;
349
}
350
351
// Lazy loading of locale strings
352
async function lazyLoadStrings(locale: string, packages: string[]) {
353
const promises = packages.map(pkg =>
354
import(`./locales/${locale}/${pkg}.json`)
355
);
356
357
const results = await Promise.all(promises);
358
359
return packages.reduce((acc, pkg, index) => {
360
acc[pkg] = results[index].default;
361
return acc;
362
}, {} as PackageLocalizedStrings);
363
}
364
365
// Build-time string optimization
366
function optimizeStringsForProduction(strings: PackageLocalizedStrings) {
367
// Remove developer comments and descriptions
368
const optimized = {};
369
370
for (const [pkg, pkgStrings] of Object.entries(strings)) {
371
optimized[pkg] = {};
372
for (const [key, value] of Object.entries(pkgStrings)) {
373
if (typeof value === "string") {
374
optimized[pkg][key] = value;
375
} else {
376
// Remove description, keep only message
377
optimized[pkg][key] = value.message;
378
}
379
}
380
}
381
382
return optimized as PackageLocalizedStrings;
383
}
384
```