# @comvi/react

API reference for @comvi/react: I18nProvider, useI18n, the selector hooks, useI18nLoader and useI18nPlugins, useFormatters, useSetLocaleTransition, and <T>.

Canonical: https://comvi.io/docs/i18n/api/react

The React binding: a provider, hooks over the i18n instance it carries, and a `<T>` component
for rich text. Subscriptions go through `useSyncExternalStore`.

## Exports

- Components: `I18nProvider`, `T`.
- Hooks: `useI18n`, `useI18nInstance`, `useLocale`, `useIsLoading`,
`useI18nLoader`, `useI18nPlugins`, `useSetLocaleTransition`, `useFormatters`.
- Re-exports: `createI18n`, `loader`, `attachLoader`, `plugins`, `definePlugin`, `icuCompiler`,
`devtools`.
- Types: `I18nProviderProps`, `MessagesMap` (the `@comvi/core` type, re-exported), `UseI18nReturn`, `UseI18nLoaderReturn`, `UseI18nPluginsReturn`,
`UseSetLocaleTransitionReturn`, `UseFormattersReturn`, `TProps`, `ComponentMap`,
`DevtoolsOptions`, `I18n`, every `@comvi/core` type.

`I18n` is a type only. `@comvi/core/tags`, the free `format*` functions, `getTextDirection`,
`MessagesCache` and `flattenCatalog` are not re-exported as values; import them from `@comvi/core`.

`src/i18n.ts`

```ts

export const i18n = createI18n({ locale: 'en', fallbackLocale: 'en' })
.use(loader({ en: () => import('./locales/en.json') }))
.use(plugins());
```

## `<I18nProvider>`

```ts
function I18nProvider(props: I18nProviderProps): JSX.Element
```

| Prop | Type | Default | Meaning |
| --- | --- | --- | --- |
| `i18n` | `AnyI18nInstance` | required | The i18n instance; any default-params type |
| `children` | `React.ReactNode` | required | The subtree |
| `autoInit` | `boolean` | `true` | Call `init()` on mount unless already initialized or initializing |
| `fallback` | `React.ReactNode` | none | Rendered instead of `children` until `isInitialized`; `false` or `null` gates blank, `undefined` does not gate |
| `ssrLocale` | `string` | none | Server-rendered locale, so the first client render matches |
| `targetLocale` | `string` | none | The locale a switch that arrives with its own markup (a route parameter) renders in: from the render it changes in until the instance's locale next changes, the tree renders in it, so the text commits with that markup. A switch that never sets the locale, a failed or cancelled load, does not end it: the tree stays in `targetLocale` until the instance's locale moves. Rendering only: you still set the instance's locale, and the target's catalog must be cached or in `messages` |
| `messages` | `MessagesMap` | none | Catalogs that arrive with the render, keyed `locale` or `locale:namespace` as `addMessages` takes them: a server handover or a route's own catalog. The render that brings them translates from them at once, through a read view; the provider adds them to the instance when that render commits, so an abandoned render and a server render never write the instance. The reference must be stable (module scope, `useMemo`, the server payload): the same object is added once, while an inline literal is a new object on every parent render, and each one merges the catalog again and re-renders every `useI18n()` consumer. Keys that name one catalog, `en` and `en:default`, merge key by key in input order, the last write winning, as `addMessages` merges them |
| `ssrInitialIsLoading` | `boolean` | `false` | Server snapshot of `isLoading` |
| `ssrInitialIsInitializing` | `boolean` | `false` | Server snapshot of `isInitializing`; `true` makes `fallback` gate the server render too |
| `onError` | `(error: Error) => void` | none | Receives an `init()` rejection; otherwise it goes to `console.error` |

Every hook throws outside the provider.

`src/App.tsx`

```tsx

export function App() {
return (
    <I18nProvider i18n={i18n} fallback={<p>Loading</p>}>
      <h1>Hello</h1>

);
}
```

## `useI18n(ns?)`

```ts
function useI18n<D extends DefaultTranslationParams = {}, NS extends string | undefined = undefined>(ns?: NS): UseI18nReturn<D, NS>
```

| Returned | Type | Meaning |
| --- | --- | --- |
| `t` | `NamespaceBoundTranslateFn<NS, D, string>` | Translate to a string; bound to `ns` and the render locale |
| `tRaw` | `NamespaceBoundTranslateFn<NS, D, TranslationResult>` | Rich-text half |
| `locale` | `string` | Current locale |
| `setLocale` | `(locale: string) => Promise<void>` | `setLocale`; resolves once loaded |
| `messagesCache` | `ReadonlyMap<string, FlattenedMessages>` | Internal map, read-only |
| `cacheRevision` | `number` | Changes on every cache mutation: `addMessages`, a load, `clearMessages` |
| `isLoading`, `isInitializing`, `isInitialized` | `boolean` | Loading flags |
| `addMessages` | `(t: Record<string, Record<string, TranslationValue>>) => void` | Merge catalogs |
| `setFallbackLocale` | `(locales: string \| string[]) => void` | Fallback chain |
| `defaultParams` | `DefaultParamsSnapshot<D>` | Shallow snapshot |
| `setDefaultParams` | `WrapperI18nInstance<D>['setDefaultParams']` | Replace defaults |
| `clearMessages` | `(locale?, namespace?) => void` | Clear content |
| `hasLocale`, `hasTranslation` | as on the instance | Non-reactive checks; `hasTranslation` defaults to the render locale, `hasLocale` takes its locale explicitly |
| `getLoadedLocales`, `getActiveNamespaces`, `getDefaultNamespace` | `() => string[]`, `() => string` | Snapshots |
| `formatNumber`, `formatDate`, `formatCurrency`, `formatRelativeTime` | bound, trailing `locale?` | Default to the render locale |
| `dir` | `'ltr' \| 'rtl'` | Text direction |
| `on` | `(event, cb) => () => void` | Subscribe |
| `reportError` | `(error, context?) => void` | Route to `onError` |

The return object is new on every render, so destructure members before putting them in a
dependency array.

`src/Greeting.tsx`

```tsx

export function Greeting() {
const { t, locale, setLocale } = useI18n();
return (
    <button onClick={() => void setLocale(locale === 'en' ? 'de' : 'en')}>
      {t('greeting', { name: 'Alice' })}
    </button>
);
}
```

## `useI18nLoader()`

```ts
function useI18nLoader(): UseI18nLoaderReturn // AcquiredLoaderApi
```

The loader members of the instance in context: `addActiveNamespace`, `addActiveNamespaces`,
`removeActiveNamespace`, `removeActiveNamespaces`, `reloadMessages`, `onLoadError`. It
throws, in development and production, when the instance has no loader capability. The result
is cached per instance, so the members are stable effect dependencies.

`src/Dashboard.tsx`

```tsx

export function Dashboard() {
const { t } = useI18n('dashboard');
const { addActiveNamespace } = useI18nLoader();

useEffect(() => {
    void addActiveNamespace('dashboard');
}, [addActiveNamespace]);

return <h1>{t('page.title')}</h1>;
}
```

## `useI18nPlugins()`

```ts
function useI18nPlugins(): UseI18nPluginsReturn // AcquiredPluginsApi
```

The plugin members of the instance in context: `onMissingKey(cb)`, returning an unsubscribe.
The callback may return a full `TranslationResult`. It throws when the instance has no plugin
capability.

## Selector hooks

```ts
function useLocale(): string
function useIsLoading(): { isLoading: boolean; isInitializing: boolean }
function useI18nInstance(): WrapperI18nInstance
```

| Hook | Meaning |
| --- | --- |
| `useLocale()` | Subscribes to locale changes only |
| `useIsLoading()` | Subscribes to loading state only |
| `useI18nInstance()` | The i18n instance itself, not reactive; for imperative calls |

## `useSetLocaleTransition()`

```ts
function useSetLocaleTransition(): { isPending: boolean; setLocale: (locale: string) => void } // returns void, unlike the instance's setLocale()
```

Wraps the locale change in `useTransition`. `isPending` stays `true` until the fetch and the
transition settle; this `setLocale` returns `void`.

## `useFormatters()`

```ts
function useFormatters(): {
formatNumber(value: number, options?: Intl.NumberFormatOptions): string;
formatDate(value: Date | number, options?: Intl.DateTimeFormatOptions): string;
formatCurrency(value: number, currency: string, options?: Intl.NumberFormatOptions): string;
formatRelativeTime(value: number, unit: Intl.RelativeTimeFormatUnit, options?: Intl.RelativeTimeFormatOptions): string;
}
```

The four formatters bound to the current locale, memoized on the locale.

## `<T>`

```ts
const T: React.NamedExoticComponent<TProps>

type ComponentMap = Record<string, ComponentHandler>
type ComponentHandler =
| string
| React.ReactElement
| ((params: { children: React.ReactNode }) => React.ReactElement)
| ({ props?: Record<string, any> } & (
      | { tag: ComponentConfigTarget; component?: ComponentConfigTarget }
      | { component: ComponentConfigTarget; tag?: ComponentConfigTarget }
    ))
type ComponentConfigTarget = string | React.ReactElement | ((props: any) => React.ReactElement)
```

| Prop | Type | Meaning |
| --- | --- | --- |
| `i18nKey` | `keyof TranslationKeys \| PermissiveKey` | Required; typed params become required with it |
| `params` | `TranslationParams` | Interpolation params; any other prop is merged in and wins a collision |
| `ns`, `locale`, `fallback` | `string` | Overrides |
| `markers` | `boolean` | `false` turns the in-context editor's markers off for this element when they cause a problem; it is then not editable in context |
| `components` | `ComponentMap` | Tag handlers: a tag name, an element, a component, or a `{ tag \| component, props }` config; `tag` wins over `component` |
| `children` | `React.ReactNode` | Rendered when the key is missing, no `fallback` was given and `onMissingKey` returned nothing |

Elements are built structurally, so translated text is never parsed as HTML.

`src/Legal.tsx`

```tsx

export function Legal() {
return (
    <>
      <T i18nKey="legal.tos" components={{ link: <a href="/terms" /> }} />
      <T i18nKey="maybe.missing">Default fallback text</T>
    </>
);
}
```

### When a tag handler throws

A handler is rendered as a component, never called, so the only guard is the error boundary
around each tag's render. A throw there is reported as
`reportError(error, { source: 'translation', tagName })` and the tag renders its own children.
The boundary is client-only and stays failed until the surrounding tree remounts.

## Read next

- [React](/docs/i18n/react): setup, loading, plurals, tags and typing for a React app.
- [`@comvi/next`](/docs/i18n/api/next): the server helpers, routing and plugin scoping.
- [`@comvi/core`](/docs/i18n/api/core): the i18n instance, the capabilities and every type.
