SDK: React
@lionrapid/react wraps the core client
for React. You build the core instance once, hand it to
LionRapidProvider, and read translations in components through the
useLionRapid hook — same keys, same generated types, plus automatic
re-rendering when translations load or the language changes.
Install the React package alongside the core (see the Quickstart for registry access):
npm install @lionrapid/core @lionrapid/reactCreate the instance and pass it to the provider via the instance prop:
import { LionRapidBuilder, MemoryRepository, NetworkRepository } from '@lionrapid/core';import { coreFormattersPlugin } from '@lionrapid/formatters';import { LionRapidProvider } from '@lionrapid/react';import ReactDOM from 'react-dom/client';import App from './App';
const lionRapid = await LionRapidBuilder.init({ defaultLocale: 'en', namespace: 'app',}) .use(new MemoryRepository({ enabled: true })) .use( new NetworkRepository({ enabled: true, options: { baseUrl: 'https://your-lionrapid-host', apiKey: import.meta.env.VITE_LIONRAPID_INTEGRATION_KEY, preloadNamespaces: [{ locale: 'en', namespace: 'app', eager: true }], }, }) ) .use(coreFormattersPlugin) .build();
ReactDOM.createRoot(document.getElementById('root')!).render( <LionRapidProvider instance={lionRapid}> <App /> </LionRapidProvider>);The useLionRapid hook
Section titled “The useLionRapid hook”useLionRapid(namespace?, options?) is the main hook:
import { useLionRapid } from '@lionrapid/react';
function Welcome() { const { t } = useLionRapid('app'); return <h1>{t('welcome')}</h1>;}It returns:
| Field | What it is |
|---|---|
t |
Translation function — plain text, params + optional default |
tHtml |
Like t but returns HTML with inline styles applied |
locale |
The current locale |
changeLanguage |
Switch locale (preloads the hook’s namespace by default) |
ready |
true once the hook’s namespace is loaded |
i18n |
The underlying core instance (i18n.bus.on(…), i18n.cache, …) |
The t returned by the hook accepts a default value as either the second
or third argument — t('key', 'Default') and
t('key', { name: 'Ada' }, 'Hello {name}!') both work.
Options:
-
keyPrefix— prepended to every key, for deeply nested key families:const { t } = useLionRapid('app', { keyPrefix: 'user.settings' });t('title'); // resolves app:user.settings.title -
autoRefresh(defaulttrue) — re-render when translations for the namespace load or the language changes. Disable for components that must never re-render from translation traffic. -
useSuspense(defaultfalse) — see loading patterns below.
Loading patterns
Section titled “Loading patterns”Translations arrive asynchronously; pick how components behave while they load.
Optimistic rendering (recommended). Just render. t() returns the
default value or the key first, and the component re-renders automatically
when the translation lands:
function Title() { const { t } = useLionRapid('app'); return <h1>{t('welcome', 'Welcome!')}</h1>;}Non-blocking indicator. Render content immediately, show progress on the side:
function Page() { const { t, ready } = useLionRapid('app'); return ( <div> {!ready && <LoadingBar />} <h1>{t('welcome')}</h1> </div> );}Blocking on ready. Only when untranslated content must never flash:
function Page() { const { t, ready } = useLionRapid('app'); if (!ready) return <Skeleton />; return <h1>{t('welcome')}</h1>;}Suspense mode (React 18+). The hook throws a promise until the
namespace is loaded, so a <Suspense> boundary shows the fallback:
function Page() { const { t } = useLionRapid('app', { useSuspense: true }); return <h1>{t('welcome')}</h1>;}Suspense mode has a 10-second safety timeout, after which the component renders with fallback values instead of suspending forever.
Changing language
Section titled “Changing language”changeLanguage from the hook preloads the hook’s namespace by default,
so components blocking on ready don’t deadlock after a switch:
await changeLanguage('fi');
// Lazy switch — skip the preload:await changeLanguage('fi', { skipPreload: true });
// Preload several namespaces:await changeLanguage('fi', { preloadNamespaces: ['app', 'errors'] });Helper hooks
Section titled “Helper hooks”-
useLocale()— the current locale, updated automatically on language change. For display components that don’t translate anything. -
useChangeLanguage()— just thechangeLanguagefunction, for language switchers:function LanguageSwitcher() {const changeLanguage = useChangeLanguage();return (<div><button onClick={() => changeLanguage('en')}>English</button><button onClick={() => changeLanguage('fi')}>Suomi</button></div>);} -
useTranslationReady(namespace?)— a boolean that flips totruewhen a namespace finishes loading (and resets on language change), for gating whole sections.
The Trans component
Section titled “The Trans component”<Trans> renders translations that contain markup as real React elements
— no dangerouslySetInnerHTML in your code.
Simple usage (no children): the translation’s inline styles are applied automatically:
<Trans i18nKey="welcome" /><Trans i18nKey="greeting" values={{ name: 'Ada' }} /><Trans i18nKey="terms" defaultValue="Read our <b>terms</b> first." />JSX interpolation: when the translation contains indexed tags (the
ContentUnit format),
child elements are matched to tags by position — the first child
renders <1>…</1>, the second <2>…</2>, and so on:
// Translation: "Hello <1>{name}</1>, you have <2>{count}</2> messages"<Trans i18nKey="dashboard.welcome" values={{ name: 'Ada', count: 5 }}> <strong /> <span className="badge" /></Trans>This is the JSX round-trip: translators see and move <1>…</1> markers,
your code decides that marker 1 is a <strong> and marker 2 is a styled
<span> — or a <Link>, or any component. ICU expressions inside the
segments ({name}, plurals, select) are processed with values.
If you pass no child for an index, the tag falls back to the style stored
on the server (bold → <strong> via the default style mapper). Props
supported: i18nKey, values, defaultValue, namespace, and children.
Under the hood <Trans> uses the exported JSXSerializer and
buildComponentMap; both are available directly if you need custom
rendering, and a custom style mapper can be injected via
new JSXSerializer({ styleMapper }).
Using generated types
Section titled “Using generated types”The types CLI augments the core’s
TranslationKeys interface, and the hook’s t resolves against the same
instance, so generated keys autocomplete in components. Import the
generated declaration file once (for example in your entry point) and keep
it inside your tsconfig.json include paths.
Testing components
Section titled “Testing components”Components that translate only need a provider with a core instance. For unit tests, build an instance with just a memory repository and seed it — no network, no mocks of the SDK itself:
import { render } from '@testing-library/react';import { LionRapidBuilder, MemoryRepository } from '@lionrapid/core';import { LionRapidProvider } from '@lionrapid/react';
async function renderWithTranslations(ui: React.ReactElement) { const memory = new MemoryRepository({ enabled: true }); memory.set('en:app:welcome', 'Welcome!');
const instance = await LionRapidBuilder.init({ defaultLocale: 'en', namespace: 'app', }) .use(memory) .build();
return render(<LionRapidProvider instance={instance}>{ui}</LionRapidProvider>);}Seed keys use the cache format locale:namespace:key. Since there is no
network handler, lookups either hit the seeded memory cache or fall back
to defaults synchronously — tests stay deterministic.
Example app
Section titled “Example app”The SDK repository ships a runnable react-simple example that exercises
every feature on this page — the hooks, loading patterns, <Trans>
showcases, and ContentUnit rendering — against a live translation server.
It is a good reference for wiring order and for what re-renders when.