# Select

> Package `@umriss-ui/core`, version 0.40.0. Demo page: <https://romanhaendler.github.io/umriss-ui/core/select/>. Every page in one line: [llms.txt](https://romanhaendler.github.io/umriss-ui/core/llms.txt); every page in full: [llms-full.txt](https://romanhaendler.github.io/umriss-ui/core/llms-full.txt).

One choice from a short, known list (dropdown). Under a mouse and the keys it opens the Combobox's list; under a finger the system's picker, a wheel on the phone.

```ts
import { Select } from "@umriss-ui/core";
```

Demo page: https://romanhaendler.github.io/umriss-ui/core/select/

The field is a native `<select>`: it holds the value and the form, and `onChange` fires as it always did. Write the choices as `<option>` and `<optgroup>`; the list is drawn from them.

An option with the empty value is the placeholder: muted in the field, and never marked as the choice in the list.

## Examples

### Select

Write the choices as native `option` elements; a disabled empty option stands in for the placeholder.

```tsx
import { useState } from "react";
import { FormField, Select } from "@umriss-ui/core";

export default function SelectExample() {
  const [centre, setCentre] = useState("");

  return (
    <FormField label="Cost centre" style={{ maxWidth: 320 }}>
      <Select value={centre} onChange={(event) => setCentre(event.target.value)}>
        <option value="" disabled>
          Choose a cost centre
        </option>
        <option value="CC-1100">CC-1100 Sales</option>
        <option value="CC-1200">CC-1200 Marketing</option>
        <option value="CC-2100">CC-2100 Engineering</option>
        <option value="CC-2200">CC-2200 Design</option>
        <option value="CC-3100">CC-3100 Customer service</option>
      </Select>
    </FormField>
  );
}
```

### States

`size="sm"` suits dense forms; errors come from the `FormField`, and a long option is cut at the field's edge.

```tsx
import { FormField, Grid, Select } from "@umriss-ui/core";

export default function States() {
  return (
    <Grid minItemWidth="220px" gap={4}>
      <FormField label="Month" hint="Small size.">
        <Select size="sm" defaultValue="2026-03">
          <option value="2026-01">January 2026</option>
          <option value="2026-02">February 2026</option>
          <option value="2026-03">March 2026</option>
        </Select>
      </FormField>
      <FormField label="VAT rate" error="Invoices from abroad are booked with reverse charge.">
        <Select defaultValue="19">
          <option value="19">19 %</option>
          <option value="7">7 %</option>
          <option value="0">Reverse charge</option>
        </Select>
      </FormField>
      <FormField label="Approver" hint="Set by the cost centre.">
        <Select disabled defaultValue="rafael">
          <option value="rafael">Rafael Ortiz</option>
        </Select>
      </FormField>
      <FormField label="Booking text">
        <Select defaultValue="long">
          <option value="long">Accrual for the annual audit fee, split over twelve months and released in December</option>
          <option value="short">Audit fee</option>
        </Select>
      </FormField>
    </Grid>
  );
}
```

### With an error

A `FormField` with an `error` marks the field invalid by itself. Without one, set `invalid` and tie your message to the field with `aria-describedby`.

```tsx
import { useId, useState } from "react";
import { Select, Stack, Text } from "@umriss-ui/core";

export default function WithAnError() {
  /* The supplier invoices from Austria, so only reverse charge is right. */
  const [rate, setRate] = useState("19");
  const messageId = useId();
  const error = rate === "0" ? undefined : "Invoices from abroad are booked with reverse charge.";

  return (
    <Stack gap={1} style={{ maxWidth: 360 }}>
      <Select
        aria-label="VAT rate, Alpenholz GmbH (Austria)"
        value={rate}
        onChange={(event) => setRate(event.target.value)}
        invalid={error !== undefined}
        aria-describedby={error ? messageId : undefined}
      >
        <option value="19">19 %</option>
        <option value="7">7 %</option>
        <option value="0">Reverse charge</option>
      </Select>
      {error && (
        <Text id={messageId} size="xs" style={{ color: "var(--u-color-danger-text)" }}>
          {error}
        </Text>
      )}
    </Stack>
  );
}
```

### Clear the choice

For an optional choice set `clearable` with `onClear`; the cross appears once something is chosen.

```tsx
import { useState } from "react";
import { FormField, Select, Stack, Text } from "@umriss-ui/core";

export default function ClearTheChoice() {
  const [owner, setOwner] = useState("helen");

  return (
    <Stack gap={3} style={{ maxWidth: 320 }}>
      <FormField label="Filter by budget owner">
        <Select clearable value={owner} onChange={(event) => setOwner(event.target.value)} onClear={() => setOwner("")}>
          <option value="" disabled>
            All owners
          </option>
          <option value="helen">Helen Marsh</option>
          <option value="rafael">Rafael Ortiz</option>
          <option value="anika">Anika Sørensen</option>
        </Select>
      </FormField>
      <Text size="xs" tone="muted" mono>
        value: "{owner}"
      </Text>
    </Stack>
  );
}
```

## When to use something else

- A long list that is searched by typing → Combobox
- Several values at once → MultiSelect
- A few options that should all stay in view or need a sentence each → RadioGroup

## Keyboard

| Key | Action |
|---|---|
| `Arrow Down / Arrow Up, Space, Alt + Arrow, F4` | Opens the list on the chosen option. Enter in a closed field is passed on, to a form or a grid. |
| `Arrow Up / Arrow Down, Home / End, Page Up / Page Down` | Moves in the open list. |
| `Typing a letter` | Jumps to the next option starting with it; in a closed field it changes the choice. |
| `Enter or Space` | Chooses the option the list stands on. |
| `Escape or Tab` | Closes the list. |

## API

### `SelectProps`

| Prop | Type | Default | Description |
|---|---|---|---|
| `size` | `"sm" \| "md"` | the size of a `ControlSizeProvider` around it, else `"md"` | `sm` for a toolbar and dense forms, `md` otherwise.<br>Shown in: [Configurator](https://romanhaendler.github.io/umriss-ui/core/select/#configurator-select), [States](https://romanhaendler.github.io/umriss-ui/core/select/#states), [Build a filter bar (Tag)](https://romanhaendler.github.io/umriss-ui/core/tag/#a-filter-bar) and 1 more |
| `chars` | `number` | — | The width in characters - room for the chosen option; the field adds its own padding, chevron and cross. Given, the field is that wide wherever it stands, and never wider than its place. Without it the field fills its place, and is 20 characters wide where the place asks - never as wide as its longest option.<br>Shown in: [Configurator](https://romanhaendler.github.io/umriss-ui/core/select/#configurator-select), [A row of fields (Sizes)](https://romanhaendler.github.io/umriss-ui/core/sizes/#a-row-of-fields) |
| `invalid` | `boolean` | — | Marks the field as invalid. `FormField` sets it itself as soon as it carries an `error` – by hand only necessary without `FormField`.<br>Shown in: [Configurator](https://romanhaendler.github.io/umriss-ui/core/select/#configurator-select), [With an error](https://romanhaendler.github.io/umriss-ui/core/select/#with-an-error) |
| `clearable` | `boolean` | `false` | Shows a × once a selection has been made, which fades in on hover/focus and empties the selection. Requires a controlled field (value) and onClear; sensible for optional selects with a placeholder option.<br>Shown in: [Clear the choice](https://romanhaendler.github.io/umriss-ui/core/select/#clear-the-choice) |
| `ref` | `Ref<HTMLSelectElement>` | — | Receives the component's root element. From `RefProp`.<br>Shown in: [A row that holds still (Sizes)](https://romanhaendler.github.io/umriss-ui/core/sizes/#a-row-that-holds-still), [An anchored surface (Popover)](https://romanhaendler.github.io/umriss-ui/core/popover/#anchored-surface), [Take the width of the trigger (Popover)](https://romanhaendler.github.io/umriss-ui/core/popover/#width-from-anchor) and 2 more |

#### Events

| Prop | Type | Default | Description |
|---|---|---|---|
| `onClear` | `() => void` | — | Called by the ×; resets the selection to the placeholder option.<br>Shown in: [Clear the choice](https://romanhaendler.github.io/umriss-ui/core/select/#clear-the-choice) |

Also takes every attribute of `<select>` – without `size`.

## Known limits

- The list shows the options' text: no icons, descriptions or search inside it - the Combobox searches.

What umriss deliberately does not build, and why: [ADR-0032](https://romanhaendler.github.io/umriss-ui/what-umriss-ui-is-not/).
