---
id: "packages/active-ui/nationality-combobox"
title: "NationalityCombobox"
description: "A searchable country or nationality choice by code, from the list and names your application supplies."
url: "/packages/active-ui/nationality-combobox"
locale: "en"
revision: "7ccdb5cf31324b06"
---

# NationalityCombobox

`NationalityCombobox` picks a country or a nationality. The value is a code, typically ISO 3166-1
alpha-2; the reader sees the name and can search on the name or the code. It is a thin layer over
[SearchCombobox](/packages/active-ui/search-combobox).

**Example: A country, with or without its code**

Client: Runs entirely in your browser. Source: `examples/ui/nationality-combobox.tsx`.

```tsx
// apps/docs/examples/ui/nationality-combobox.tsx
'use client';

import {useMemo, useState} from 'react';

import {NationalityCombobox} from '@workspace/active-ui/components/composed/nationality-combobox';
import {
  Field,
  FieldDescription,
  FieldLabel,
} from '@workspace/active-ui/components/primitives/field';
import {Switch} from '@workspace/active-ui/components/primitives/switch';

// The application's list and order: two common countries first, then the rest.
const FIRST = ['NL', 'BE'];
const REST = ['DE', 'FR', 'ES', 'IT', 'PL', 'PT', 'SE', 'TR', 'MA', 'GB', 'US'];

/**
 * A country by its ISO 3166-1 code, with names from `Intl.DisplayNames`.
 * The code is searchable even when it is not shown; switch on "Show codes"
 * to see it before the name.
 */
export default function NationalityComboboxExample() {
  const [country, setCountry] = useState('');
  const [showCode, setShowCode] = useState(false);
  const options = useMemo(() => {
    const names = new Intl.DisplayNames(['en'], {type: 'region'});
    const named = (code: string) => ({code, name: names.of(code) ?? code});
    return [
      ...FIRST.map(named),
      ...REST.map(named).sort((a, b) => a.name.localeCompare(b.name, 'en')),
    ];
  }, []);
  return (
    <div className="grid w-full max-w-xs gap-6">
      <Field orientation="horizontal">
        <Switch
          id="show-codes"
          checked={showCode}
          onCheckedChange={setShowCode}
        />
        <FieldLabel htmlFor="show-codes">Show codes</FieldLabel>
      </Field>
      <Field>
        <FieldLabel htmlFor="nationality">Nationality</FieldLabel>
        <NationalityCombobox
          id="nationality"
          value={country}
          onValueChange={setCountry}
          options={options}
          showCode={showCode}
          locale="en"
          placeholder="Choose a country"
          searchLabel="Search countries"
          searchPlaceholder="Search…"
          emptyText="No country found."
          aria-describedby="nationality-value"
        />
        <FieldDescription id="nationality-value">
          Value: <code>{country || "''"}</code>
        </FieldDescription>
      </Field>
    </div>
  );
}
```

## What the composition adds

- **A code as the value**, `''` when nothing is chosen.
- **The code is searchable** even when it is not shown. `showCode` (default `false`) shows it before
  the name, `NL - Netherlands`, where the reader also reads codes.

It owns no list, no names and no order: a subset, a historical entry or common countries first are
simply a different array.

## Usage

```tsx
import {NationalityCombobox} from '@workspace/active-ui/components/composed/nationality-combobox';

<NationalityCombobox
  id="nationality"
  value={country}
  onValueChange={setCountry}
  options={[{code: 'NL', name: 'Netherlands'}]}
  locale="en"
  placeholder="Choose a country"
  searchLabel="Search countries"
  searchPlaceholder="Search…"
  emptyText="No country found."
/>;
```

## What your application provides

- The countries in the order to show, with names in the reader's language; the example uses
  `Intl.DisplayNames` and puts two countries first.
- The translated texts; there are no English defaults.

## Accessibility

As [SearchCombobox](/packages/active-ui/search-combobox): name the button with `FieldLabel htmlFor`
its `id`; `aria-describedby` and `aria-invalid` go to the button.

## API

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `emptyText` | `string` | yes | Text when the search finds nothing. Supplied translated. |
| `locale` | `string` | yes | The reader's language, for matching the query against the names, as `SearchCombobox.locale` does. It does not produce the names: those arrive in `options`. |
| `onValueChange` | `(value: string) => void` | yes | Receives the chosen code, or `''` when the choice is cleared. |
| `options` | `readonly NationalityComboboxOption[]` | yes | The complete set of countries or nationalities to choose from, **in the order they are shown**.  Required, and there is no built-in list: which entries exist, what they are called in the reader's language, and which come first are reference data and project policy, owned by the caller. A subset, an extra territory or a historical entry is simply a different array. An empty array is valid and shows `emptyText`. |
| `placeholder` | `string` | yes | Text on the button while nothing is chosen. Supplied translated.  Required, like every text below: an empty value is a valid state of this field, and a blank button or a blank empty state tells the user nothing. There is no default and no English fallback: the same contract as `SearchCombobox`. |
| `searchLabel` | `string` | yes | Accessible name of the search field inside the menu. Supplied translated. |
| `searchPlaceholder` | `string` | yes | Text in the search field inside the menu. Supplied translated. |
| `value` | `string` | yes | The chosen code, or `''` when nothing is chosen. |
| `aria-describedby` | `string` | no | The ids of the texts that describe this choice: a `FieldDescription`, a `FieldError`, or both separated by a space. |
| `aria-invalid` | `"false" \| "grammar" \| "spelling" \| "true" \| boolean` | no | The validation state of the field, set by the caller from its form state (`fieldState.invalid`). It lands on the button that carries the choice, where assistive technology reads it and the field's invalid border keys on it. This component decides nothing about validity itself. |
| `className` | `string` | no | Classes for the button. |
| `disabled` | `boolean` | no | Disables the button. |
| `id` | `string` | no | The `id` of the button that carries the choice, so a visible label can name it programmatically with `htmlFor`.  Generic and without a domain word: this component does not know which field it is. The prop exists because a `FieldLabel` without `htmlFor` above a Base UI trigger is only visually a label; the button's accessible name stayed empty. |
| `showCode` | `boolean` | no | Shows the code before the name: `NL - Nederland`.  **Off by default**: for a person, a nationality is a word and not a code. The code still takes part in searching.  Turn it on where the field stores a country code and the reader also reads that code, such as an administration form; `CurrencyCombobox` shows its codes in the same form (`EUR - Euro`).  The order does not change: it stays the order of `options`. |

**NationalityComboboxOption**

```ts
interface NationalityComboboxOption {
  code: string;
  name: string;
}
```

One country or nationality the caller offers.

The shape a reference-data source typically produces: a code and a
translated name. This package imports no such source; the caller maps
its own data to this shape.
