Babelize Elements
ComponentsLanguage Switchers

Language Switcher

Copy-paste language switcher component for React with dropdown, search, RTL support, and full accessibility. Works with next-intl, react-i18next, and any i18n setup.

Hello, world!

Installation

bash npx shadcn@latest add @elements/language-switcher

Usage

import { LanguageSwitcher } from "@/components/ui/language-switcher";
<LanguageSwitcher
  locales={[
    { code: "en" },
    { code: "fr" },
    { code: "es" },
    { code: "de" },
    { code: "ja" },
    { code: "ar" },
  ]}
  defaultValue="en"
  onValueChange={(code) => console.log(code)}
/>

Props

PropTypeRequiredDescription
localesLocale[]YesArray of available locales — only code is required
valuestringNoSelected locale code — pass this to control the component
defaultValuestringNoInitial selected locale code (default: first locale's code)
onValueChange(code: string) => voidNoCallback when locale changes
label"native" | "english"NoDisplay labels in native or English (default: "english")
showFlagsbooleanNoShow flag emojis (default: false)
classNamestringNoAdditional CSS classes

Locale Object

PropertyTypeDescription
codestringISO 639-1 / BCP 47 code (e.g. "en", "fr-FR", "ar-SA") — required
labelstringOverride display name (auto-resolved from built-in mapping if omitted)
flagstringOverride flag emoji (auto-detected from code if omitted)
rtlbooleanOverride RTL (auto-detected from code if omitted)

Examples

With Babelize SDK

Pair Language Switcher with the Babelize SDK for automatic translations:

"use client";

import { useState } from "react";
import { useRouter } from "next/navigation";
import { LanguageSwitcher } from "@/components/ui/language-switcher";
import { useBabelize } from "@babelize/sdk/react";

const locales = [
  { code: "en", label: "English", flag: "🇺🇸" },
  { code: "fr", label: "Français", flag: "🇫🇷" },
  { code: "es", label: "Español", flag: "🇪🇸" },
  { code: "de", label: "Deutsch", flag: "🇩🇪" },
  { code: "ja", label: "日本語", flag: "🇯🇵" },
  { code: "ar", label: "العربية", flag: "🇸🇦" },
];

export function LocaleSwitcher() {
  const router = useRouter();
  const { locale, setLocale } = useBabelize();

  return (
    <LanguageSwitcher
      locales={locales}
      value={locale}
      onValueChange={(code) => {
        setLocale(code);
        router.push(`/${code}`);
      }}
    />
  );
}

With next-intl

"use client";

import { useRouter } from "next/navigation";
import { LanguageSwitcher } from "@/components/ui/language-switcher";

export function LocaleSwitcher() {
  const router = useRouter();

  return (
    <LanguageSwitcher
      locales={[{ code: "en" }, { code: "fr" }]}
      defaultValue="en"
      onValueChange={(code) => router.push(`/${code}`)}
    />
  );
}

With react-i18next

"use client";

import { useTranslation } from "react-i18next";
import { LanguageSwitcher } from "@/components/ui/language-switcher";

export function LocaleSwitcher() {
  const { i18n } = useTranslation();

  return (
    <LanguageSwitcher
      locales={[{ code: "en" }, { code: "fr" }]}
      value={i18n.language}
      onValueChange={(code) => i18n.changeLanguage(code)}
    />
  );
}

Native Labels + Flags

"use client";

import { LanguageSwitcher } from "@/components/ui/language-switcher";

export default function App() {
  return (
    <LanguageSwitcher
      locales={[{ code: "en" }, { code: "fr" }, { code: "es" }]}
      defaultValue="en"
      label="native"
      showFlags
    />
  );
}

Features

  • Zero-config — just pass codes, labels and flags are auto-resolved
  • Dropdown with search — filter locales by name or code
  • RTL-aware — auto-detects RTL locales (ar, he, fa, ur) and adjusts layout
  • Keyboard accessible — arrow keys, Enter, Escape, Tab navigation
  • ARIA labels — proper aria-expanded, aria-haspopup, aria-selected
  • Dark mode — built with Tailwind CSS, dark-mode compatible
  • Library agnostic — works with any i18n setup via callback

Supported Languages

Labels and flags are auto-detected from the language code. Pass just the code:

CodeLanguageFlagRTL
enEnglish🇺🇸
frFrançais🇫🇷
esEspañol🇪🇸
deDeutsch🇩🇪
ja日本語🇯🇵
ko한국어🇰🇷
zh中文🇨🇳
arالعربية🇸🇦
ptPortuguês🇧🇷
itItaliano🇮🇹
nlNederlands🇳🇱
ruРусский🇷🇺
plPolski🇵🇱
trTürkçe🇹🇷
viTiếng Việt🇻🇳
thไทย🇹🇭
idBahasa Indonesia🇮🇩
hiहिन्दी🇮🇳
bnবাংলা🇧🇩
ukУкраїнська🇺🇦
csČeština🇨🇿
svSvenska🇸🇪
daDansk🇩🇰
fiSuomi🇫🇮
noNorsk🇳🇴
elΕλληνικά🇬🇷
heעברית🇮🇱
faفارسی🇮🇷
roRomână🇷🇴
huMagyar🇭🇺
skSlovenčina🇸🇰
bgБългарски🇧🇬
hrHrvatski🇭🇷
srСрпски🇷🇸
slSlovenščina🇸🇮
ltLietuvių🇱🇹
lvLatviešu🇱🇻
etEesti🇪🇪
gaGaeilge🇮🇪
caCatalà🇦🇩
isÍslenska🇮🇸
swKiswahili🇹🇿
amአማርኛ🇪🇹
neनेपाली🇳🇵
siසිංහල🇱🇰
myမြန်မာ🇲🇲
kmខ្មែរ🇰🇭
loລາວ🇱🇦
kaქართული🇬🇪
hyՀայերեն🇦🇲
kkҚазақ🇰🇿
uzOʻzbek🇺🇿
mnМонгол🇲🇳
urاردو🇵🇰
mlമലയാളം🇮🇳
taதமிழ்🇮🇳
teతెలుగు🇮🇳
knಕನ್ನಡ🇮🇳
mrमराठी🇮🇳
guગુજરાતી🇮🇳
paਪੰਜਾਬੀ🇮🇳
yoYorùbá🇳🇬
igIgbo🇳🇬
zuisiZulu🇿🇦
afAfrikaans🇿🇦
haHausa🇳🇬
tlFilipino🇵🇭
miTe Reo Māori🇳🇿
smGagana Samoa🇼🇸
toLea Faka-Tonga🇹🇴
eoEsperanto🌐

Need a language not listed? Pass a custom label and flag to override:

<LanguageSwitcher
  locales={[{ code: "xyz", label: "My Language", flag: "🏴" }]}
  defaultValue="xyz"
/>

Dependencies

This component uses:

  • clsx and tailwind-merge (bundled as dependencies)

Peer dependencies:

  • react >= 18
  • react-dom >= 18
  • tailwindcss >= 3

Credits

Created by sapatmohit.

On this page