All blocks
Language Select
Locale picker with native language names, English hints, and BCP-47 values — ready for i18n and profile settings forms.
Built withSelect
Language
Install
1npx stylexblocks add language-selectSource
components/blocks/base-ui/language-select.tsx
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110import * as stylex from "@stylexjs/stylex";import { useMemo } from "react";import { Select, type SelectVariantProps } from "#components/ui/base-ui/select.tsx";import { semanticColors } from "@stylexblocks/ui/semantic-tokens/colors.stylex.ts";import type { StyleOverrideProps } from "#lib/recipe-props.ts";
export interface Language { /** BCP-47 language tag, e.g. "pt-BR" — what your backend receives. */ value: string; /** Native name, shown in the control. */ label: string; /** English name, shown as a secondary hint in the list. */ english: string;}
export const languages: Language[] = [ { value: "en", label: "English", english: "English" }, { value: "zh-Hans", label: "简体中文", english: "Chinese (Simplified)" }, { value: "nl", label: "Nederlands", english: "Dutch" }, { value: "fr", label: "Français", english: "French" }, { value: "de", label: "Deutsch", english: "German" }, { value: "it", label: "Italiano", english: "Italian" }, { value: "ja", label: "日本語", english: "Japanese" }, { value: "ko", label: "한국어", english: "Korean" }, { value: "pt-BR", label: "Português (Brasil)", english: "Portuguese (Brazil)" }, { value: "es", label: "Español", english: "Spanish" },];
const styles = stylex.create({ english: { color: semanticColors.fgSubtle, fontSize: "0.8125em", marginInlineStart: "auto", paddingInlineStart: "0.75rem", },});
export interface LanguageSelectProps extends SelectVariantProps, StyleOverrideProps { label?: string; /** BCP-47 tag of the initial selection. */ defaultValue?: string; /** BCP-47 tag of the controlled selection. */ value?: string; onValueChange?: (locale: string) => void; /** Posts the selection with native form submits via a hidden input. */ name?: string; /** Override the offered locales; defaults to the ten most common. */ items?: Language[]; disabled?: boolean;}
/** * LanguageSelect — locale picker for settings and onboarding forms. Shows * native language names with English hints, speaks BCP-47 on the wire. */export function LanguageSelect({ label = "Language", defaultValue, value, onValueChange, name, items = languages, disabled, variant, size, palette, style,}: LanguageSelectProps) { // Base UI resolves the trigger's display label from Root's `items` map. const displayItems = useMemo( () => items.map((lang) => ({ value: lang.value, label: lang.label })), [items], ); return ( <Select.Root items={displayItems} name={name} disabled={disabled} defaultValue={defaultValue} value={value} onValueChange={(locale) => onValueChange?.((locale as string) ?? "")} variant={variant} size={size} palette={palette} style={style} > <Select.Label>{label}</Select.Label> <Select.Control> <Select.Trigger> <Select.Value /> </Select.Trigger> <Select.IndicatorGroup> <Select.Icon /> </Select.IndicatorGroup> </Select.Control> <Select.Positioner> <Select.Popup> {items.map((lang) => ( <Select.Item key={lang.value} value={lang.value}> <Select.ItemText>{lang.label}</Select.ItemText> <span {...stylex.props(styles.english)}>{lang.english}</span> <Select.ItemIndicator /> </Select.Item> ))} </Select.Popup> </Select.Positioner> </Select.Root> );}