Client-Side Features
En esta página
Client-side features allow instant locale switching without a full page reload or server round-trip. When BetterI18nProvider is present in the tree, locale changes trigger a CDN fetch and client re-render — no navigation needed.
BetterI18nProvider #
Wrap your Root Layout with BetterI18nProvider to enable instant client-side locale switching via useSetLocale.
import { BetterI18nProvider } from '@better-i18n/next/client';
import { i18n } from '@/i18n.config';
export default async function LocaleLayout({ children, params }) {
const { locale } = await params;
const messages = await i18n.getMessages(locale);
return (
<BetterI18nProvider config={i18n.config} locale={locale} messages={messages}>
{children}
</BetterI18nProvider>
);
}Props #
| Prop | Type | Default | Description |
|---|---|---|---|
config | I18nConfig | Required | The i18n config from createI18n() |
locale | string | From cookie | Override active locale |
messages | Messages | — | Pre-loaded SSR messages |
timeZone | string | config.timeZone | IANA timezone |
now | Date | — | Pin "now" for SSR hydration consistency |
useSetLocale() #
Switches the active locale. Behavior depends on whether BetterI18nProvider is present in the tree.
With BetterI18nProvider (instant) #
When the provider is in the tree, calling setLocale triggers an instant CDN fetch and client
re-render. No page navigation or router.refresh() is needed.
'use client';
import { useSetLocale } from '@better-i18n/next/client';
function LanguageSwitcher() {
const setLocale = useSetLocale();
return (
<button onClick={() => setLocale('tr')}>
Switch to Turkish
</button>
);
}Without BetterI18nProvider (soft refresh) #
When called outside a provider, useSetLocale updates the locale cookie and calls
router.refresh() to trigger a soft server re-render.
'use client';
import { useSetLocale } from '@better-i18n/next/client';
import { i18n } from '@/i18n.config';
function LanguageSwitcher() {
// Pass config explicitly when no provider is in the tree
const setLocale = useSetLocale({ config: i18n.config });
return (
<select onChange={(e) => setLocale(e.target.value)}>
<option value="en">English</option>
<option value="tr">Türkçe</option>
</select>
);
}Behavior Comparison #
| Mode | Trigger | Result |
|---|---|---|
With BetterI18nProvider | setLocale('tr') | Instant CDN fetch + client re-render |
| Without provider | setLocale('tr') | Cookie update + router.refresh() |
With provider + localePrefix: "never" | setLocale('tr') | Instant CDN fetch + cookie update (URL unchanged) |
Without provider + localePrefix: "never" | setLocale('tr') | Cookie update + router.refresh() (URL unchanged) |
With
localePrefix: "never", the URL never changes during locale switching — only the cookie and rendered content update. This makes it ideal for dashboards and SaaS apps where the URL should remain stable across languages.
useManifestLanguages(config) #
Fetches available languages with full metadata on the client. Includes request deduplication.
'use client';
import { useManifestLanguages } from '@better-i18n/next/client';
import { i18n } from '@/i18n.config';
function LanguageSwitcher() {
const { languages, isLoading } = useManifestLanguages(i18n.config);
const setLocale = useSetLocale();
if (isLoading) return <select disabled><option>Loading...</option></select>;
return (
<select onChange={(e) => setLocale(e.target.value)}>
{languages.map((lang) => (
<option key={lang.code} value={lang.code}>
{lang.nativeName || lang.name || lang.code}
</option>
))}
</select>
);
}Return Value #
| Property | Type | Description |
|---|---|---|
languages | LanguageOption[] | Available languages from manifest |
isLoading | boolean | true while fetching |
error | Error | null | Error if fetch failed |
LocaleDropdown #
A fully-featured, accessible locale switcher dropdown for Next.js. Import from @better-i18n/next/client — the RSC-safe client entrypoint.
'use client';
import { LocaleDropdown } from '@better-i18n/next/client';
import { useLocale } from 'next-intl';
import { i18n } from '@/i18n.config';
function Header() {
const locale = useLocale();
return (
<header>
<LocaleDropdown config={i18n.config} locale={locale} />
</header>
);
}Props #
| Prop | Type | Default | Description |
|---|---|---|---|
config | I18nConfig | Required | Config from createI18n() |
locale | string | Required | Current locale from useLocale() |
languages | LanguageOption[] | — | Pre-fetched languages (skips client CDN fetch) |
variant | "styled" | "unstyled" | "styled" | Styled or unstyled mode |
showFlag | boolean | true | Show flag emoji/image |
showNativeName | boolean | true | Show native language name |
showLocaleCode | boolean | true | Show locale code |
renderTrigger | (ctx) => ReactNode | — | Custom trigger renderer |
renderItem | (ctx) => ReactNode | — | Custom item renderer |
Locale Switching Behavior #
| Context | Behavior |
|---|---|
Inside BetterI18nProvider | Instant CDN fetch + client re-render — no navigation |
| Without provider | Cookie update + router.refresh() — soft server re-render |
SSR-Optimized Example #
Pass languages from the server to skip the client-side CDN fetch:
import { BetterI18nProvider } from '@better-i18n/next/client';
import { i18n } from '@/i18n.config';
export default async function LocaleLayout({ children, params }) {
const { locale } = await params;
const [messages, languages] = await Promise.all([
i18n.getMessages(locale),
i18n.getLanguages(),
]);
return (
<BetterI18nProvider
config={i18n.config}
locale={locale}
messages={messages}
>
<Header locale={locale} languages={languages} />
{children}
</BetterI18nProvider>
);
}'use client';
import { LocaleDropdown } from '@better-i18n/next/client';
import { i18n } from '@/i18n.config';
import type { LanguageOption } from '@better-i18n/core';
function Header({ locale, languages }: { locale: string; languages: LanguageOption[] }) {
return (
<header>
<LocaleDropdown config={i18n.config} locale={locale} languages={languages} />
</header>
);
}For full props reference, variants, CSS custom properties, and custom rendering — see LocaleDropdown.
Better I18N