Better I18NBetter I18N
Next.js

Client-Side Features

Client-side components and hooks for instant locale switching in Next.js

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.

app/[locale]/layout.tsx
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>
  );
}

BetterI18nProvider enables instant client-side locale switching via useSetLocale — no page navigation or router refresh needed.

Props

PropTypeDefaultDescription
configI18nConfigRequiredThe i18n config from createI18n()
localestringFrom cookieOverride active locale
messagesMessagesPre-loaded SSR messages
timeZonestringconfig.timeZoneIANA timezone
nowDatePin "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

ModeTriggerResult
With BetterI18nProvidersetLocale('tr')Instant CDN fetch + client re-render
Without providersetLocale('tr')Cookie update + router.refresh()

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

PropertyTypeDescription
languagesLanguageOption[]Available languages from manifest
isLoadingbooleantrue while fetching
errorError | nullError if fetch failed

On this page