React usage

@18ways/react gives you the runtime primitives. The main job is to scope them cleanly.

Root mode vs scope mode

At the root, Ways owns the API key, locale, accepted locales, and shared translation store.

Nested Ways scopes only need context:

tsx
<Ways apiKey="pk_dummy_demo_token" locale="es-ES" baseLocale="en-GB" context="checkout">
  <Ways context="payment-form">
    <PaymentForm />
  </Ways>
</Ways>

If a subtree needs its own locale session, give it its own root instead of relying on a scoped locale override:

tsx
<Ways apiKey="pk_dummy_demo_token" locale="en-GB" baseLocale="en-GB" context="app">
  <MarketingSite />
 
  <Ways apiKey="pk_dummy_demo_token" locale="ja-JP" baseLocale="en-GB" context="hero-demo">
    <EmbeddedLocaleDemo />
  </Ways>
</Ways>

That nested root gets its own selected locale, loading state, and hydrated store.

Loading state

If you want to reflect translation work in the UI, use useTranslationLoading().

tsx
import { useTranslationLoading } from '@18ways/react';
 
function SavePanel() {
  const isTranslationLoading = useTranslationLoading();
 
  return <span>{isTranslationLoading ? 'Loading translations...' : 'Ready'}</span>;
}

useTranslationLoading() only tracks blocking work for the current context. Root-level controls such as LanguageSwitcher already read the root runtime loading state for you.

Suspense

<T> and useT() throw to the nearest React or framework boundary when a non-transition read is still pending. During a real locale transition, they keep rendering the previous settled locale via the store fallback instead of suspending. Blocking work is capped by suspenseTimeoutMs, after which rendering continues with the best available fallback while translation loading carries on in the background.

Locale hooks

The root runtime exposes two low-level hooks:

tsx
import { useCurrentLocale, useSetCurrentLocale } from '@18ways/react';

Use them when you want to build your own locale controls instead of LanguageSwitcher.

LanguageSwitcher

The built-in switcher is controlled and composable.

tsx
<LanguageSwitcher currentLocale={locale} onLocaleChange={setLocale} direction="down" />

It reads the accepted locale list from the root runtime and handles loading states while the next locale resolves. During a locale transition, translated UI keeps using the last settled locale as its fallback until the next locale is ready.

Component composition

Keep the sentence together and let <T> preserve the UI shape.

tsx
<T>
  <a href="/pricing">Click here</a> to see more
</T>

Message formatter escape hatches

The default formatter is waysParser. Only switch away from it if you have a concrete reason.

  • messageFormatter="none" leaves strings alone.
  • a custom formatter function lets you own interpolation fully.

That should be rare.