React integration for i18next internationalization framework with hooks, components, and SSR support
—
React i18next provides higher-order components (HOCs) for injecting translation functionality into class components and legacy React patterns, as well as server-side rendering support.
Higher-order component that injects translation props into wrapped components, providing an alternative to hooks for class components or when hooks aren't suitable.
/**
* HOC that injects translation props into wrapped components
* @param ns - Namespace(s) for translations
* @param options - Configuration options including ref forwarding and key prefix
* @returns HOC function that wraps components with translation props
*/
function withTranslation<
Ns extends FlatNamespace | $Tuple<FlatNamespace> | undefined = undefined,
KPrefix extends KeyPrefix<FallbackNs<Ns>> = undefined
>(
ns?: Ns,
options?: {
/** Enable ref forwarding to wrapped component */
withRef?: boolean;
/** Prefix for all translation keys */
keyPrefix?: KPrefix;
}
): <
C extends React.ComponentType<React.ComponentProps<any> & WithTranslationProps>
>(
component: C
) => React.ComponentType<Omit<React.ComponentProps<C>, keyof WithTranslation<Ns>> & WithTranslationProps>;
// Props injected by withTranslation HOC
interface WithTranslation<
Ns extends FlatNamespace | $Tuple<FlatNamespace> | undefined = undefined,
KPrefix extends KeyPrefix<FallbackNs<Ns>> = undefined
> {
/** Translation function with type-safe keys */
t: TFunction<FallbackNs<Ns>, KPrefix>;
/** i18next instance for language changes and configuration */
i18n: i18n;
/** Indicates if translations are loaded and ready */
tReady: boolean;
}
// Props that can be passed to wrapped component
interface WithTranslationProps {
/** Custom i18next instance */
i18n?: i18n;
/** Enable React Suspense mode */
useSuspense?: boolean;
}Usage Examples:
import { withTranslation, WithTranslation } from "react-i18next";
// Class component with injected translation props
interface Props extends WithTranslation {
username: string;
}
class UserProfile extends React.Component<Props> {
handleLanguageChange = (lang: string) => {
this.props.i18n.changeLanguage(lang);
};
render() {
const { t, tReady, username } = this.props;
if (!tReady) return <div>Loading...</div>;
return (
<div>
<h1>{t('profile.title')}</h1>
<p>{t('profile.welcome', { name: username })}</p>
<button onClick={() => this.handleLanguageChange('es')}>
{t('switchLanguage')}
</button>
</div>
);
}
}
export default withTranslation('user')(UserProfile);
// Functional component with HOC (alternative to useTranslation)
function Settings({ t, i18n, tReady }: WithTranslation) {
if (!tReady) return <div>Loading...</div>;
return (
<div>
<h2>{t('settings.title')}</h2>
<button onClick={() => i18n.reloadResources()}>
{t('settings.reload')}
</button>
</div>
);
}
export default withTranslation('common')(Settings);
// With namespace and key prefix
const TranslatedComponent = withTranslation('dashboard', {
keyPrefix: 'widgets'
})(function Widget({ t }) {
return <div>{t('title')}</div>; // Resolves to 'dashboard:widgets.title'
});
// With ref forwarding
class RefComponent extends React.Component {
focus() {
// Component method
}
render() {
const { t } = this.props;
return <input placeholder={t('placeholder')} />;
}
}
const TranslatedRefComponent = withTranslation('forms', {
withRef: true
})(RefComponent);
// Usage with ref
function Parent() {
const ref = useRef(null);
return (
<div>
<TranslatedRefComponent ref={ref} />
<button onClick={() => ref.current?.focus()}>
Focus Input
</button>
</div>
);
}
// Multiple namespaces
const MultiNSComponent = withTranslation(['common', 'user'])(
function Profile({ t }) {
return (
<div>
<h1>{t('user:title')}</h1>
<button>{t('common:save')}</button>
</div>
);
}
);Higher-order component that adds server-side rendering support to components, including getInitialProps method for data fetching.
/**
* HOC providing SSR support with getInitialProps method
* @returns HOC function that adds SSR capabilities to wrapped components
*/
function withSSR(): <Props>(
WrappedComponent: React.ComponentType<Props>
) => {
(props: {
initialI18nStore: Resource;
initialLanguage: string;
} & Props): React.FunctionComponentElement<Props>;
getInitialProps: (ctx: unknown) => Promise<{
initialI18nStore: Resource;
initialLanguage: string;
}>;
};Usage Examples:
import { withSSR, WithTranslation, withTranslation } from "react-i18next";
// Component with SSR support
interface PageProps extends WithTranslation {
data: any;
}
class HomePage extends React.Component<PageProps> {
static async getInitialProps(ctx) {
// Fetch page-specific data
const data = await fetchPageData(ctx);
return { data };
}
render() {
const { t, data } = this.props;
return (
<div>
<h1>{t('home.title')}</h1>
<div>{data.content}</div>
</div>
);
}
}
// Combine with translation HOC and SSR HOC
export default withSSR()(withTranslation('home')(HomePage));
// Functional component with SSR
function ProductPage({ product, t, initialI18nStore, initialLanguage }) {
return (
<div>
<h1>{t('product.title', { name: product.name })}</h1>
<p>{t('product.price', { price: product.price })}</p>
</div>
);
}
ProductPage.getInitialProps = async (ctx) => {
const product = await fetchProduct(ctx.query.id);
return { product };
};
export default withSSR()(withTranslation('products')(ProductPage));
// Next.js usage
export default function Page(props) {
return <HomePage {...props} />;
}
export const getServerSideProps = HomePage.getInitialProps;
// Usage in Next.js App component
class MyApp extends App {
static async getInitialProps(appContext) {
const appProps = await App.getInitialProps(appContext);
// Get SSR translation data
const { Component } = appContext;
if (Component.getInitialProps) {
const pageProps = await Component.getInitialProps(appContext.ctx);
return { ...appProps, pageProps };
}
return appProps;
}
render() {
const { Component, pageProps } = this.props;
return <Component {...pageProps} />;
}
}// Multiple HOCs composition
const EnhancedComponent = withSSR()(
withTranslation('namespace')(
MyComponent
)
);
// With custom display names
function MyComponent({ t, customProp }) {
return <div>{t('title')} - {customProp}</div>;
}
const Enhanced = withTranslation('custom')(MyComponent);
Enhanced.displayName = 'TranslatedMyComponent';
// Type-safe HOC composition
interface ComponentProps {
customProp: string;
}
const TypedComponent: React.FC<ComponentProps & WithTranslation> = ({ t, customProp }) => (
<div>{t('message')} {customProp}</div>
);
export default withTranslation()(TypedComponent);// Traditional class component with lifecycle methods
class LegacyComponent extends React.Component<WithTranslation & { userId: string }> {
componentDidMount() {
const { i18n, userId } = this.props;
// Load user-specific translations
i18n.loadNamespaces(['user-specific']);
}
componentDidUpdate(prevProps) {
const { i18n, userId } = this.props;
if (prevProps.userId !== userId) {
// Reload translations for new user
i18n.reloadResources();
}
}
render() {
const { t, tReady, userId } = this.props;
if (!tReady) {
return <div>{t('loading')}</div>;
}
return (
<div>
<h1>{t('user.welcome', { id: userId })}</h1>
<p>{t('user.status')}</p>
</div>
);
}
}
export default withTranslation(['common', 'user'])(LegacyComponent);// Helper types for component props manipulation
type $Subtract<T extends K, K> = Omit<T, keyof K>;
// Fallback namespace resolution
type FallbackNs<Ns> = Ns extends undefined
? TypeOptions['defaultNS']
: Ns extends Namespace
? Ns
: TypeOptions['defaultNS'];
// HOC return type helpers
type HOCResult<C, InjectedProps> = React.ComponentType<
Omit<React.ComponentProps<C>, keyof InjectedProps> & WithTranslationProps
>;When migrating from HOCs to hooks:
// Before: HOC pattern
class OldComponent extends React.Component<WithTranslation> {
render() {
const { t } = this.props;
return <div>{t('message')}</div>;
}
}
export default withTranslation()(OldComponent);
// After: Hook pattern
function NewComponent() {
const { t } = useTranslation();
return <div>{t('message')}</div>;
}
export default NewComponent;withRef: true when you need to access wrapped component methodsInstall with Tessl CLI
npx tessl i tessl/npm-react-i18next