Repository navigation
Client Components
📄 Mirrors
docs/client-components.mdin the main repo — the repo version is canonical.
Status: planned / pre-release. This page documents the intended API of next-tongue. Nothing is published yet; names may change before v1.0. See ROADMAP.
TongueProvider (in your [locale] layout, see Getting started) provides the locale (and optionally a timeZone for useFormat, see ADR 0008). It does not take a messages prop.
Outside [locale] (global app/not-found.tsx, the static-export root app/page.tsx) there is no provider: useT throws TONGUE_E_NO_PROVIDER. Wrap such trees in <TongueProvider locale={defaultLocale}> deliberately (ADR 0002).
The cheapest client messages are none. Render text in a Server Component and pass it down:
// app/[locale]/page.tsx (Server Component)
const t = await getT(locale, 'counter');
return <Counter label={t('label')} />; // Counter is a client component; no message code shipsUse useT only when the client needs to format at runtime (e.g. a count that changes with state). See ADR 0009.
'use client';
import { useT } from 'next-tongue/client';
export function Counter() {
const t = useT('counter');
const [n, setN] = useState(0);
return <button onClick={() => setN(n + 1)}>{t('clicked', { count: n })}</button>;
}A per-file loader rewrites each t('key') call in a client module into a static import of a per-key message module (#tongue/msg/{namespace}/{key}). The bundler then includes only the keys that client code calls, deduplicated across routes.
- Default: a per-key module contains all locales of that key and picks the provider's locale. No extra request, no Suspense.
- Opt-in (later): per-locale splitting with dynamic imports and a Suspense contract, once the benchmark has measured when it pays off.
- If
tescapes the file (passed as a prop or argument, returned, stored in an object), the loader includes the whole namespace and warns withfile:line.
No JSON catalog is serialised into the RSC payload or HTML. See Architecture.
t(`status.${status}`) // status: 'open' | 'closed'Non-literal keys must declare their possible keys; the loader has no type information, so it cannot infer a union from status's type.
t.dynamic(`status.${status}`, { from: ['status.open', 'status.closed'] }); // imports only these two
const s = t.ns('status'); s(status); // imports the whole `status` namespaceAn undeclared non-literal key (t(someString)) is a build error, TONGUE_E_DYNAMIC_KEY.
| Hook | Returns |
|---|---|
useLocale() |
Locale from the provider |
useFormat() |
Same API as getFormat (number, dateTime, relativeTime, list) |
useRouter(), usePathname()
|
from next-tongue/navigation
|
next-tongue/navigation ships a headless, accessible switcher (v0.3, #44). It keeps the current page (including localized pathnames and dynamic params), persists the choice through the locale cookie (or the signed preference, see User preferences), and labels each locale in its own language via Intl.DisplayNames.
'use client';
import { LocaleSwitcher } from 'next-tongue/navigation';
export function LanguageMenu() {
return (
<LocaleSwitcher>
{(options) => (
<nav aria-label="Language">
{options.map(({ locale, label, href, current }) => (
<a key={locale} href={href} hrefLang={locale} lang={locale} aria-current={current ? 'true' : undefined}>{label}</a>
))}
</nav>
)}
</LocaleSwitcher>
);
}It renders plain links, so it works without JavaScript. useLocaleSwitcher() returns the same options for custom UIs (e.g. a <select>).
(locales is the configured tuple, inlined at build time; it is also exported from next-tongue/server. next-tongue/config is build-time only and must not be imported from app code.)
Guides
- Getting Started
- Configuration
- Routing and Proxy
- Server Components
- Client Components
- Server Actions
- ICU Messages
- Type Safety
- Cache Components and PPR
- Static Export
- Metadata SEO and hreflang
- Database and CMS Messages
- Dev Tools
- Translator Workflow
- Per-user Preferred Language
- CLI
- AI Translation
- Testing
- VS Code Extension
- Migration from next intl
- Migration from next international (deferred)
Reference