Country Select
A button-triggered searchable country popover returning the ISO 3166-1 alpha-2 code.
- Status
- Since
0.1.0- Accessibility pattern
- APG combobox (dialog popup)
Last updated
Install
Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Country Select before installation. Retain the digest printed by this command.
pnpm exec vegastack-design verify country-selectInstall with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.
pnpm dlx shadcn@4.21.0 add @vegastack/country-selectRun the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.
The same command installs the registry items it composes: @vegastack/searchable-select, @vegastack/geo-data.
Usage
import { CountrySelect } from "@/components/ui/country-select";
const [country, setCountry] = useState<string>();
<CountrySelect value={country} onValueChange={setCountry} />;CountrySelect is controlled: pass the selected country's ISO 3166-1 alpha-2 code as value and
update it from onValueChange. The component is a single composite — there are no subcomponents to
compose.
Examples
Anatomy
CountrySelect is a thin, data-fed wrapper over
SearchableSelect: the dataset goes in, the ISO code comes
out, and every structural decision — the Select-shaped trigger, the search field inside the panel,
the check on the selected row, the clear control — belongs to that preset.
<CountrySelect value={code} onValueChange={setCode} />
// ↓ renders, internally:
// <SearchableSelect
// items={countries} ← the geo-data COUNTRIES array by default
// value={selected} onValueChange={…} ← the ONE selection path
// renderItem={…} ← flag + name per row
// searchLabel="Search countries" ← the in-panel search field
// />The trigger is w-full, like every other form control — constrain it with a parent
(<div className="max-w-xs">), not with a width of its own; the panel matches the trigger's width.
Country selection
A flag + name list with type-to-filter search inside a popup; the value is the ISO 3166-1 alpha-2 code.
Selected & disabled
With a value set, the trigger shows the country's flag + name and the matching row carries a check;
disabled renders an inert trigger that won't open.
Empty
Nothing selected yet, so the trigger shows a muted placeholder. Open it and type to filter — when no country matches the search, the list shows the empty-results message "No country found."
Custom dataset
Pass a countries array to restrict or extend the offered list (it defaults to the full
COUNTRIES set from geo-data). The selected value is still the ISO code.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
aria-label | string | — | Accessible name for the trigger. Defaults to the selected country name or the placeholder. |
className | string | — | Additional class names merged onto the trigger button. |
clearable | boolean | false | Shows a clear control on the trigger while a country is selected. |
countries | Country[] | COUNTRIES | Override the country list. |
disabled | boolean | false | Disable the control. |
id | string | — | id forwarded to the trigger for label association. |
onValueChange | ((code: string) => void) | — | Fired with the selected ISO code when the user picks a country, or "" when it is cleared. |
placeholder | string | 'Select country' | Placeholder shown on the trigger when nothing is selected. |
ref | React.Ref<HTMLButtonElement> | — | Ref forwarded to the trigger button — the component's focusable root (the popover panel is portaled, so the trigger is the stable host element to focus/measure). |
value | string | — | The selected country's ISO 3166-1 alpha-2 code (controlled). |
Data attributes and CSS variables on CountrySelect
| Attribute | Values |
|---|---|
data-slot | "country-select" |
Data API
The dataset is its own registry item, geo-data, installed automatically as a dependency of this
component and shared with RegionSelect — install both and the ISO
data lands in your tree once. Import from your lib alias, not from the component.
Country
| Prop | Type | Default | Description |
|---|---|---|---|
code* | string | — | ISO 3166-1 alpha-2 code, stored as the selected value (e.g. "US"). |
flag* | string | — | The flag emoji (regional indicator pair). |
name* | string | — | The human-readable country name shown in the list (e.g. "United States"). |
COUNTRIES
| Prop | Type | Default | Description |
|---|---|---|---|
COUNTRIES | Country[] | — | The full dataset — 198 ISO 3166-1 alpha-2 entries (code + English name + flag emoji), covering all regions. The default value of the countries prop. Extend it by editing the array; codes are unique. |
getCountryByCode(code)
| Prop | Type | Default | Description |
|---|---|---|---|
getCountryByCode | (code: string | undefined) => Country | undefined | — | Case-insensitive lookup of a country by its ISO 3166-1 alpha-2 code. Returns undefined for an unknown or missing code. |
import { getCountryByCode } from "@/lib/geo-data";
getCountryByCode("us")?.name; // "United States" (case-insensitive)
getCountryByCode("ZZ"); // undefinedAccessibility
- The trigger carries
role="combobox", an explicitaria-label(the selected country name, or the placeholder), andaria-expanded/aria-haspopup="dialog"(Base UI's Select-style combobox pattern — the search input lives inside the popup). - The list is keyboard-navigable (↑/↓/Enter); typing filters by country name or code.
- Flag emoji are
aria-hidden(decorative); the name carries the accessible text. - With
clearable, the clear control is a sibling of the trigger — never a child — so the trigger never becomes an interactive control containing another one.
| Key | Action |
|---|---|
| Enter / Space | Open the popover from the trigger. |
| ↑ / ↓ | Move the highlight between matching countries. |
| Enter | Select the highlighted country and close. |
| Esc | Close without changing the selection. |
| Contract | States tested |
|---|---|
| Behaviour | default, disabled, selected |
| Accessibility | disabled, labeled |
| Visual | default |
Do / Don't
Field Type Link Step
The "Links to" step of FieldTypePicker — grouped kinds of record, each explained in one line, and a table choice for Rows.
Region Select
A searchable combobox of states/provinces for a country — Combobox-powered filtering, with a free-text fallback for countries with no subdivisions.