React integration for i18next internationalization framework with hooks, components, and SSR support
—
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.
Hook for initializing i18next with pre-loaded translations and language settings from the server.
/**
* Hook for SSR initialization with pre-loaded translation data
* @param initialI18nStore - Translation resources loaded on server
* @param initialLanguage - Language detected/set on server
* @param props - Optional configuration with custom i18next instance
*/
function useSSR(
initialI18nStore: Resource,
initialLanguage: string,
props?: { i18n?: i18n }
): void;
interface Resource {
[language: string]: {
[namespace: string]: any;
};
}Usage Examples:
import { useSSR, useTranslation } from "react-i18next";
// Next.js page component
function HomePage({ initialI18nStore, initialLanguage }) {
useSSR(initialI18nStore, initialLanguage);
const { t } = useTranslation();
return (
<div>
<h1>{t('welcome')}</h1>
<p>{t('description')}</p>
</div>
);
}
export async function getServerSideProps(context) {
// Get initial props on server
const { initialI18nStore, initialLanguage } = getInitialProps();
return {
props: {
initialI18nStore,
initialLanguage
}
};
}
// With custom i18next instance
function CustomSSRPage({ ssrData, customI18n }) {
useSSR(ssrData.store, ssrData.language, { i18n: customI18n });
const { t } = useTranslation();
return <div>{t('content')}</div>;
}
// Remix loader example
export async function loader({ request }) {
const { initialI18nStore, initialLanguage } = getInitialProps();
return json({
initialI18nStore,
initialLanguage
});
}
export default function RemixPage() {
const { initialI18nStore, initialLanguage } = useLoaderData();
useSSR(initialI18nStore, initialLanguage);
const { t } = useTranslation();
return <h1>{t('title')}</h1>;
}Functions for generating initial translation data on the server.
/**
* Generates initial props for SSR with translation data
* @returns Object with translation store and current language
*/
function getInitialProps(): {
initialI18nStore: { [ns: string]: {} };
initialLanguage: string;
};
/**
* Composes initial props from component and translation data
* @param ForComponent - Component with potential getInitialProps method
* @returns Async function that combines component and i18n initial props
*/
function composeInitialProps(ForComponent: any): (ctx: unknown) => Promise<{
[key: string]: any;
initialI18nStore: { [ns: string]: {} };
initialLanguage: string;
}>;Usage Examples:
import { getInitialProps, composeInitialProps } from "react-i18next";
// Basic initial props generation
export async function getServerSideProps() {
const i18nProps = getInitialProps();
return {
props: {
...i18nProps,
// Additional server-side data
serverTime: new Date().toISOString()
}
};
}
// Composing with component initial props
class ProductPage extends React.Component {
static async getInitialProps(ctx) {
const productId = ctx.query.id;
const product = await fetchProduct(productId);
return { product };
}
render() {
const { t, product } = this.props;
return (
<div>
<h1>{t('product.title', { name: product.name })}</h1>
<p>{t('product.price', { price: product.price })}</p>
</div>
);
}
}
// Combine component and i18n initial props
ProductPage.getInitialProps = composeInitialProps(ProductPage);
// Manual composition
export async function getServerSideProps(context) {
// Get component-specific data
const productId = context.query.id;
const product = await fetchProduct(productId);
// Get i18n data
const { initialI18nStore, initialLanguage } = getInitialProps();
return {
props: {
product,
initialI18nStore,
initialLanguage
}
};
}Higher-order component that automatically adds SSR support to components.
/**
* HOC that adds SSR support with automatic getInitialProps generation
* @returns HOC function that wraps components with SSR capabilities
*/
function withSSR(): <Props>(
WrappedComponent: React.ComponentType<Props>
) => {
(props: {
initialI18nStore: Resource;
initialLanguage: string;
} & Props): React.FunctionComponentElement<Props>;
getInitialProps: (ctx: unknown) => Promise<any>;
};Usage Examples:
import { withSSR, withTranslation } from "react-i18next";
// Component with automatic SSR support
class BlogPost extends React.Component {
static async getInitialProps(ctx) {
const postId = ctx.query.id;
const post = await fetchBlogPost(postId);
return { post };
}
render() {
const { t, post } = this.props;
return (
<article>
<h1>{t('blog.title', { title: post.title })}</h1>
<div>{post.content}</div>
</article>
);
}
}
// Apply both translation and SSR HOCs
export default withSSR()(withTranslation('blog')(BlogPost));
// Functional component with SSR
function NewsPage({ articles, t }) {
return (
<div>
<h1>{t('news.headline')}</h1>
{articles.map(article => (
<div key={article.id}>
<h2>{article.title}</h2>
<p>{article.summary}</p>
</div>
))}
</div>
);
}
NewsPage.getInitialProps = async () => {
const articles = await fetchLatestNews();
return { articles };
};
export default withSSR()(withTranslation('news')(NewsPage));Complete Next.js setup with SSR support:
// pages/_app.js
import { appWithTranslation } from 'next-i18next';
import { I18nextProvider } from 'react-i18next';
import i18n from '../i18n/config';
function MyApp({ Component, pageProps }) {
return (
<I18nextProvider i18n={i18n}>
<Component {...pageProps} />
</I18nextProvider>
);
}
export default appWithTranslation(MyApp);
// pages/index.js
import { useSSR, useTranslation } from 'react-i18next';
import { getInitialProps } from 'react-i18next';
function HomePage(props) {
useSSR(props.initialI18nStore, props.initialLanguage);
const { t } = useTranslation();
return <h1>{t('welcome')}</h1>;
}
export async function getServerSideProps() {
return {
props: getInitialProps()
};
}
export default HomePage;
// Alternative with getStaticProps
export async function getStaticProps({ locale }) {
return {
props: {
...getInitialProps(),
// Additional static props
}
};
}// app/root.tsx
import { useSSR } from 'react-i18next';
import { useLoaderData } from '@remix-run/react';
export async function loader() {
const { initialI18nStore, initialLanguage } = getInitialProps();
return json({ initialI18nStore, initialLanguage });
}
export default function App() {
const { initialI18nStore, initialLanguage } = useLoaderData();
useSSR(initialI18nStore, initialLanguage);
return (
<html>
<head />
<body>
<Outlet />
</body>
</html>
);
}
// app/routes/index.tsx
export default function Index() {
const { t } = useTranslation();
return <h1>{t('welcome')}</h1>;
}// server.js (Express example)
import express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { I18nextProvider } from 'react-i18next';
import { getInitialProps } from 'react-i18next';
import App from './App';
import i18n from './i18n/config';
const server = express();
server.get('*', async (req, res) => {
// Initialize i18n for this request
const { initialI18nStore, initialLanguage } = getInitialProps();
// Render React app to string
const html = renderToString(
<I18nextProvider i18n={i18n}>
<App
initialI18nStore={initialI18nStore}
initialLanguage={initialLanguage}
/>
</I18nextProvider>
);
res.send(`
<!DOCTYPE html>
<html>
<body>
<div id="root">${html}</div>
<script>
window.__INITIAL_I18N_STORE__ = ${JSON.stringify(initialI18nStore)};
window.__INITIAL_LANGUAGE__ = "${initialLanguage}";
</script>
</body>
</html>
`);
});React i18next automatically tracks used namespaces for SSR optimization:
/**
* Namespace usage tracking for SSR optimization
*/
interface ReportNamespaces {
/** Add namespaces to the used list */
addUsedNamespaces(namespaces: Namespace): void;
/** Get list of namespaces used during rendering */
getUsedNamespaces(): string[];
}Usage Examples:
// Automatic namespace tracking
function Component() {
const { t } = useTranslation(['common', 'user']); // Automatically tracked
return <div>{t('common:welcome')}</div>;
}
// Manual namespace reporting
import { getI18n } from 'react-i18next';
const i18n = getI18n();
if (i18n.reportNamespaces) {
i18n.reportNamespaces.addUsedNamespaces(['admin', 'dashboard']);
}
// Server-side namespace collection
export async function getServerSideProps() {
const i18n = getI18n();
// Render app to collect used namespaces
renderToString(<App />);
// Get only the namespaces that were actually used
const usedNamespaces = i18n.reportNamespaces?.getUsedNamespaces() || [];
return {
props: {
...getInitialProps(),
usedNamespaces
}
};
}Ensure client and server render the same content:
// Avoid hydration mismatches
function SafeComponent({ initialI18nStore, initialLanguage }) {
useSSR(initialI18nStore, initialLanguage);
const { t, ready } = useTranslation();
// Wait for client-side hydration
if (!ready) {
return <div>Loading...</div>; // Same as server
}
return <div>{t('content')}</div>;
}// Load only required namespaces
export async function getServerSideProps() {
const requiredNamespaces = ['common', 'home'];
const i18n = getI18n();
await Promise.all(
requiredNamespaces.map(ns => i18n.loadNamespaces(ns))
);
return {
props: getInitialProps()
};
}function SSRErrorBoundary({ children, initialI18nStore, initialLanguage }) {
try {
useSSR(initialI18nStore, initialLanguage);
return children;
} catch (error) {
console.error('SSR initialization failed:', error);
// Fallback to client-side initialization
return <div>Loading...</div>;
}
}Install with Tessl CLI
npx tessl i tessl/npm-react-i18next