0
# Server-Side Rendering
1
2
React i18next provides comprehensive server-side rendering (SSR) support for Next.js, Remix, and other SSR frameworks, including initial data hydration and SSR-safe component rendering.
3
4
## Capabilities
5
6
### useSSR Hook
7
8
Hook for initializing i18next with pre-loaded translations and language settings from the server.
9
10
```typescript { .api }
11
/**
12
* Hook for SSR initialization with pre-loaded translation data
13
* @param initialI18nStore - Translation resources loaded on server
14
* @param initialLanguage - Language detected/set on server
15
* @param props - Optional configuration with custom i18next instance
16
*/
17
function useSSR(
18
initialI18nStore: Resource,
19
initialLanguage: string,
20
props?: { i18n?: i18n }
21
): void;
22
23
interface Resource {
24
[language: string]: {
25
[namespace: string]: any;
26
};
27
}
28
```
29
30
**Usage Examples:**
31
32
```typescript
33
import { useSSR, useTranslation } from "react-i18next";
34
35
// Next.js page component
36
function HomePage({ initialI18nStore, initialLanguage }) {
37
useSSR(initialI18nStore, initialLanguage);
38
const { t } = useTranslation();
39
40
return (
41
<div>
42
<h1>{t('welcome')}</h1>
43
<p>{t('description')}</p>
44
</div>
45
);
46
}
47
48
export async function getServerSideProps(context) {
49
// Get initial props on server
50
const { initialI18nStore, initialLanguage } = getInitialProps();
51
52
return {
53
props: {
54
initialI18nStore,
55
initialLanguage
56
}
57
};
58
}
59
60
// With custom i18next instance
61
function CustomSSRPage({ ssrData, customI18n }) {
62
useSSR(ssrData.store, ssrData.language, { i18n: customI18n });
63
const { t } = useTranslation();
64
65
return <div>{t('content')}</div>;
66
}
67
68
// Remix loader example
69
export async function loader({ request }) {
70
const { initialI18nStore, initialLanguage } = getInitialProps();
71
72
return json({
73
initialI18nStore,
74
initialLanguage
75
});
76
}
77
78
export default function RemixPage() {
79
const { initialI18nStore, initialLanguage } = useLoaderData();
80
useSSR(initialI18nStore, initialLanguage);
81
82
const { t } = useTranslation();
83
return <h1>{t('title')}</h1>;
84
}
85
```
86
87
### Initial Props Generation
88
89
Functions for generating initial translation data on the server.
90
91
```typescript { .api }
92
/**
93
* Generates initial props for SSR with translation data
94
* @returns Object with translation store and current language
95
*/
96
function getInitialProps(): {
97
initialI18nStore: { [ns: string]: {} };
98
initialLanguage: string;
99
};
100
101
/**
102
* Composes initial props from component and translation data
103
* @param ForComponent - Component with potential getInitialProps method
104
* @returns Async function that combines component and i18n initial props
105
*/
106
function composeInitialProps(ForComponent: any): (ctx: unknown) => Promise<{
107
[key: string]: any;
108
initialI18nStore: { [ns: string]: {} };
109
initialLanguage: string;
110
}>;
111
```
112
113
**Usage Examples:**
114
115
```typescript
116
import { getInitialProps, composeInitialProps } from "react-i18next";
117
118
// Basic initial props generation
119
export async function getServerSideProps() {
120
const i18nProps = getInitialProps();
121
122
return {
123
props: {
124
...i18nProps,
125
// Additional server-side data
126
serverTime: new Date().toISOString()
127
}
128
};
129
}
130
131
// Composing with component initial props
132
class ProductPage extends React.Component {
133
static async getInitialProps(ctx) {
134
const productId = ctx.query.id;
135
const product = await fetchProduct(productId);
136
137
return { product };
138
}
139
140
render() {
141
const { t, product } = this.props;
142
return (
143
<div>
144
<h1>{t('product.title', { name: product.name })}</h1>
145
<p>{t('product.price', { price: product.price })}</p>
146
</div>
147
);
148
}
149
}
150
151
// Combine component and i18n initial props
152
ProductPage.getInitialProps = composeInitialProps(ProductPage);
153
154
// Manual composition
155
export async function getServerSideProps(context) {
156
// Get component-specific data
157
const productId = context.query.id;
158
const product = await fetchProduct(productId);
159
160
// Get i18n data
161
const { initialI18nStore, initialLanguage } = getInitialProps();
162
163
return {
164
props: {
165
product,
166
initialI18nStore,
167
initialLanguage
168
}
169
};
170
}
171
```
172
173
### withSSR HOC
174
175
Higher-order component that automatically adds SSR support to components.
176
177
```typescript { .api }
178
/**
179
* HOC that adds SSR support with automatic getInitialProps generation
180
* @returns HOC function that wraps components with SSR capabilities
181
*/
182
function withSSR(): <Props>(
183
WrappedComponent: React.ComponentType<Props>
184
) => {
185
(props: {
186
initialI18nStore: Resource;
187
initialLanguage: string;
188
} & Props): React.FunctionComponentElement<Props>;
189
getInitialProps: (ctx: unknown) => Promise<any>;
190
};
191
```
192
193
**Usage Examples:**
194
195
```typescript
196
import { withSSR, withTranslation } from "react-i18next";
197
198
// Component with automatic SSR support
199
class BlogPost extends React.Component {
200
static async getInitialProps(ctx) {
201
const postId = ctx.query.id;
202
const post = await fetchBlogPost(postId);
203
return { post };
204
}
205
206
render() {
207
const { t, post } = this.props;
208
return (
209
<article>
210
<h1>{t('blog.title', { title: post.title })}</h1>
211
<div>{post.content}</div>
212
</article>
213
);
214
}
215
}
216
217
// Apply both translation and SSR HOCs
218
export default withSSR()(withTranslation('blog')(BlogPost));
219
220
// Functional component with SSR
221
function NewsPage({ articles, t }) {
222
return (
223
<div>
224
<h1>{t('news.headline')}</h1>
225
{articles.map(article => (
226
<div key={article.id}>
227
<h2>{article.title}</h2>
228
<p>{article.summary}</p>
229
</div>
230
))}
231
</div>
232
);
233
}
234
235
NewsPage.getInitialProps = async () => {
236
const articles = await fetchLatestNews();
237
return { articles };
238
};
239
240
export default withSSR()(withTranslation('news')(NewsPage));
241
```
242
243
## Framework Integration
244
245
### Next.js Integration
246
247
Complete Next.js setup with SSR support:
248
249
```typescript
250
// pages/_app.js
251
import { appWithTranslation } from 'next-i18next';
252
import { I18nextProvider } from 'react-i18next';
253
import i18n from '../i18n/config';
254
255
function MyApp({ Component, pageProps }) {
256
return (
257
<I18nextProvider i18n={i18n}>
258
<Component {...pageProps} />
259
</I18nextProvider>
260
);
261
}
262
263
export default appWithTranslation(MyApp);
264
265
// pages/index.js
266
import { useSSR, useTranslation } from 'react-i18next';
267
import { getInitialProps } from 'react-i18next';
268
269
function HomePage(props) {
270
useSSR(props.initialI18nStore, props.initialLanguage);
271
const { t } = useTranslation();
272
273
return <h1>{t('welcome')}</h1>;
274
}
275
276
export async function getServerSideProps() {
277
return {
278
props: getInitialProps()
279
};
280
}
281
282
export default HomePage;
283
284
// Alternative with getStaticProps
285
export async function getStaticProps({ locale }) {
286
return {
287
props: {
288
...getInitialProps(),
289
// Additional static props
290
}
291
};
292
}
293
```
294
295
### Remix Integration
296
297
```typescript
298
// app/root.tsx
299
import { useSSR } from 'react-i18next';
300
import { useLoaderData } from '@remix-run/react';
301
302
export async function loader() {
303
const { initialI18nStore, initialLanguage } = getInitialProps();
304
return json({ initialI18nStore, initialLanguage });
305
}
306
307
export default function App() {
308
const { initialI18nStore, initialLanguage } = useLoaderData();
309
useSSR(initialI18nStore, initialLanguage);
310
311
return (
312
<html>
313
<head />
314
<body>
315
<Outlet />
316
</body>
317
</html>
318
);
319
}
320
321
// app/routes/index.tsx
322
export default function Index() {
323
const { t } = useTranslation();
324
return <h1>{t('welcome')}</h1>;
325
}
326
```
327
328
### Custom SSR Setup
329
330
```typescript
331
// server.js (Express example)
332
import express from 'express';
333
import React from 'react';
334
import { renderToString } from 'react-dom/server';
335
import { I18nextProvider } from 'react-i18next';
336
import { getInitialProps } from 'react-i18next';
337
import App from './App';
338
import i18n from './i18n/config';
339
340
const server = express();
341
342
server.get('*', async (req, res) => {
343
// Initialize i18n for this request
344
const { initialI18nStore, initialLanguage } = getInitialProps();
345
346
// Render React app to string
347
const html = renderToString(
348
<I18nextProvider i18n={i18n}>
349
<App
350
initialI18nStore={initialI18nStore}
351
initialLanguage={initialLanguage}
352
/>
353
</I18nextProvider>
354
);
355
356
res.send(`
357
<!DOCTYPE html>
358
<html>
359
<body>
360
<div id="root">${html}</div>
361
<script>
362
window.__INITIAL_I18N_STORE__ = ${JSON.stringify(initialI18nStore)};
363
window.__INITIAL_LANGUAGE__ = "${initialLanguage}";
364
</script>
365
</body>
366
</html>
367
`);
368
});
369
```
370
371
## Namespace Reporting
372
373
React i18next automatically tracks used namespaces for SSR optimization:
374
375
```typescript { .api }
376
/**
377
* Namespace usage tracking for SSR optimization
378
*/
379
interface ReportNamespaces {
380
/** Add namespaces to the used list */
381
addUsedNamespaces(namespaces: Namespace): void;
382
/** Get list of namespaces used during rendering */
383
getUsedNamespaces(): string[];
384
}
385
```
386
387
**Usage Examples:**
388
389
```typescript
390
// Automatic namespace tracking
391
function Component() {
392
const { t } = useTranslation(['common', 'user']); // Automatically tracked
393
return <div>{t('common:welcome')}</div>;
394
}
395
396
// Manual namespace reporting
397
import { getI18n } from 'react-i18next';
398
399
const i18n = getI18n();
400
if (i18n.reportNamespaces) {
401
i18n.reportNamespaces.addUsedNamespaces(['admin', 'dashboard']);
402
}
403
404
// Server-side namespace collection
405
export async function getServerSideProps() {
406
const i18n = getI18n();
407
408
// Render app to collect used namespaces
409
renderToString(<App />);
410
411
// Get only the namespaces that were actually used
412
const usedNamespaces = i18n.reportNamespaces?.getUsedNamespaces() || [];
413
414
return {
415
props: {
416
...getInitialProps(),
417
usedNamespaces
418
}
419
};
420
}
421
```
422
423
## SSR Best Practices
424
425
### Hydration Safety
426
427
Ensure client and server render the same content:
428
429
```typescript
430
// Avoid hydration mismatches
431
function SafeComponent({ initialI18nStore, initialLanguage }) {
432
useSSR(initialI18nStore, initialLanguage);
433
const { t, ready } = useTranslation();
434
435
// Wait for client-side hydration
436
if (!ready) {
437
return <div>Loading...</div>; // Same as server
438
}
439
440
return <div>{t('content')}</div>;
441
}
442
```
443
444
### Performance Optimization
445
446
```typescript
447
// Load only required namespaces
448
export async function getServerSideProps() {
449
const requiredNamespaces = ['common', 'home'];
450
451
const i18n = getI18n();
452
await Promise.all(
453
requiredNamespaces.map(ns => i18n.loadNamespaces(ns))
454
);
455
456
return {
457
props: getInitialProps()
458
};
459
}
460
```
461
462
### Error Handling
463
464
```typescript
465
function SSRErrorBoundary({ children, initialI18nStore, initialLanguage }) {
466
try {
467
useSSR(initialI18nStore, initialLanguage);
468
return children;
469
} catch (error) {
470
console.error('SSR initialization failed:', error);
471
// Fallback to client-side initialization
472
return <div>Loading...</div>;
473
}
474
}
475
```