Installation

Install the SDK, wire up the init file, and render your first translation.

Install

npm install @api18n/react
# or
bun add @api18n/react
# or
pnpm add @api18n/react

react is a peer dependency at version 18 or higher. The SDK works with React 18, 19, and Next.js 14 / 15 / 16.

Pair with the CLI

Most setups pair the SDK with @api18n/cli, which generates the JSON files the SDK reads:

npm install --save-dev @api18n/cli

Then in your project root:

npx api18n init && npx api18n login && npx api18n pull

That writes messages/{locale}.json and messages/messages.d.ts. See the CLI installation guide for the full flow.

Create the init file

This is the only place you'll touch the SDK setup. Conventionally src/api18n.ts:

// src/api18n.ts
import { createApi18n } from '@api18n/react';
import en from './messages/en.json';
import ptBR from './messages/pt-BR.json';

export const api18n = createApi18n({
  resources: { en, 'pt-BR': ptBR },
  defaultLocale: 'en',
  fallbackLocale: 'en',
});

createApi18n registers a singleton. After this runs, useTranslations(), useLocale(), setLocale() all work anywhere in your app.

Dynamic imports for code-splitting

If you have many locales and want each one in its own chunk, dynamic- import them instead:

import { createApi18n } from '@api18n/react';

const en = (await import('./messages/en.json')).default;

export const api18n = createApi18n({
  resources: { en },
  defaultLocale: 'en',
});

export async function loadLocale(code: string) {
  const messages = await import(`./messages/${code}.json`);
  // Use the runtime helpers to merge — see API reference.
}

Most apps with ≤ 8 locales eagerly import all of them: the parsed JSON is tiny (~10–50 KB gzipped per locale) and the cost of round-trip dynamic imports outweighs the bundle savings.

Mount the init file

The init file has to run before any component renders. Side-effect import it at the top of your entry point.

Vite / CRA

// src/main.tsx
import './api18n';
import { App } from './App';

createRoot(document.getElementById('root')!).render(<App />);

Next.js App Router

// app/layout.tsx
import '../src/api18n';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}

For mixed client / server component apps, you also call setRequestLocale in the layout — see API reference: Server entry.

First component

// app/page.tsx (Next.js)  or  src/App.tsx (Vite)
'use client'; // only needed in Next.js App Router

import { useTranslations } from '@api18n/react';

export default function Home() {
  const t = useTranslations();
  return <h1>{t('welcome', { name: 'world' })}</h1>;
}

Assuming messages/en.json has { "welcome": "Hello {name}" }, this renders Hello world.

Enable type safety

With the CLI's typegen on, you'll have messages/messages.d.ts next to your JSON files. Make sure TypeScript picks it up — either:

  1. Trust auto-discovery (TS finds .d.ts files in include paths automatically), or
  2. Add the path explicitly in tsconfig.json:
{
  "include": ["src", "messages/messages.d.ts"]
}

Now t('helo', { name: 'x' }) fails with Argument of type '"helo"' is not assignable to parameter of type 'TranslationKey', and missing args fail the same way. Pull the CLI's messages.d.ts into PRs and your code review starts catching i18n typos before they ship.