CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-i18next

React integration for i18next internationalization framework with hooks, components, and SSR support

Pending
Overview
Eval results
Files

ssr.mddocs/

Server-Side Rendering

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.

Capabilities

useSSR Hook

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>;
}

Initial Props Generation

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
    }
  };
}

withSSR HOC

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));

Framework Integration

Next.js Integration

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
    }
  };
}

Remix Integration

// 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>;
}

Custom SSR Setup

// 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>
  `);
});

Namespace Reporting

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
    }
  };
}

SSR Best Practices

Hydration Safety

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>;
}

Performance Optimization

// 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()
  };
}

Error Handling

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

docs

components.md

hocs.md

hooks.md

icu-macro.md

index.md

ssr.md

tile.json