Client-Side Features
Components and hooks for locale switching in Vite + React applications
Client-side features in @better-i18n/use-intl are built for CSR apps. Locale switching happens entirely in React state — no server round-trips, no page navigation required.
useLocale
Access and switch the current locale from anywhere inside BetterI18nProvider:
import { useLocale } from '@better-i18n/use-intl'
function LocaleToggle() {
const { locale, setLocale, isLoading } = useLocale()
return (
<button
onClick={() => setLocale(locale === 'en' ? 'tr' : 'en')}
disabled={isLoading}
>
{isLoading ? 'Switching...' : `Current: ${locale}`}
</button>
)
}Return Value
| Property | Type | Description |
|---|---|---|
locale | string | Current active locale (e.g., "en", "tr") |
setLocale | (locale: string) => void | Switch locale — triggers CDN fetch + re-render |
isLoading | boolean | true while new messages are loading |
Locale Switching Flow
Calling setLocale() triggers:
- CDN fetch for the new locale's messages
- React context update with new translations
- Full re-render of the component tree
onLocaleChangecallback (fromBetterI18nProvider) fired with the new locale
LanguageSwitcher
A drop-in <select>-based language switcher. Handles loading state and locale changes automatically.
import { LanguageSwitcher } from '@better-i18n/use-intl'
function Header() {
return (
<header className="flex justify-between items-center p-4">
<Logo />
<LanguageSwitcher className="border rounded px-3 py-2" />
</header>
)
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | CSS class for the <select> element |
loadingLabel | string | "Loading..." | Label shown while fetching languages |
renderOption | (lang) => ReactNode | — | Custom option renderer |
LocaleDropdown
A fully-featured accessible dropdown with flag icons, keyboard navigation, and CSS theming.
import { LocaleDropdown } from '@better-i18n/use-intl'
function Header() {
return (
<header>
<LocaleDropdown />
</header>
)
}Selecting a locale calls setLocale() internally — no props needed inside BetterI18nProvider.
Quick Theming
Override CSS custom properties on any parent element:
.my-header {
--better-locale-text: #374151;
--better-locale-trigger-bg: transparent;
--better-locale-border: #e5e7eb;
--better-locale-menu-bg: #ffffff;
--better-locale-hover-bg: #f3f4f6;
--better-locale-active-bg: #f9fafb;
--better-locale-code-text: #9ca3af;
}Unstyled Mode
Use variant="unstyled" with Tailwind or any CSS framework:
<LocaleDropdown
variant="unstyled"
className="relative"
triggerClassName="flex items-center gap-2 px-3 py-2 rounded-lg hover:bg-gray-100"
menuClassName="absolute right-0 mt-1 w-52 bg-white border rounded-xl shadow-lg z-50"
/>For the full API — props, CSS custom properties, data attributes, renderTrigger, renderItem, and keyboard shortcuts — see Locale Management.
Persisting the Locale
With localeCookie prop (recommended)
The localeCookie prop handles both reading and writing the locale cookie automatically:
import { BetterI18nProvider, LocaleDropdown } from '@better-i18n/use-intl'
function App() {
return (
<BetterI18nProvider localeCookie> {}
<LocaleDropdown />
<Router />
</BetterI18nProvider>
)
}This writes a locale cookie on every locale change, and reads it back on page refresh. No manual document.cookie or localStorage needed.
With Vite Plugin (SSR)
When using @better-i18n/vite, set the same cookie name on both the plugin and provider:
betterI18n({
project: "your-org/your-project",
localeCookie: "locale",
})<BetterI18nProvider localeCookie> {/* matches "locale" by default */}
<App />
</BetterI18nProvider>The Vite plugin reads the cookie server-side during SSR, and the provider reads it client-side for SPA navigation.
Custom cookie name
Use a string value to set a custom cookie name:
<BetterI18nProvider localeCookie="preferred-locale">
<App />
</BetterI18nProvider>The cookie name must match across client, server, and Vite plugin. See Provider docs for the full server-side pairing pattern.