Select
Use select with the current published Askr packages.
@askrjs/ui/select0.0.13@askrjs/themes/select0.0.13
How to use select
Select should use the standard themed surface unless the application is deliberately implementing its own design system on top of @askrjs/ui.
- Import the themed component from @askrjs/themes/components; use @askrjs/ui directly only when building a custom visual system.
- Keep select labels, state, and application actions visible at the call site instead of styling internal DOM nodes.
- Verify keyboard behavior, focus movement or return, disabled state, and both standard themes.
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@askrjs/themes/components';
<Select value={status()} onValueChange={status.set}>
<SelectTrigger aria-label="Project status"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="active">Active</SelectItem>
<SelectItem value="paused">Paused</SelectItem>
</SelectContent>
</Select>Purpose
Select gives you a native-feeling listbox trigger without dictating how it looks. The headless `@askrjs/ui/select` primitive tracks the open state, the selected value, and keyboard navigation; `@askrjs/themes/select` layers on the visual trigger, popover content, and item styling you see in the demos. Reach for it when a user needs to pick exactly one value from a list that's too long or too structured for a radio group.
Install and import
Add both `@askrjs/ui` and `@askrjs/themes` (the headless primitive needs the accompanying visuals to look like anything). Import from the dedicated subpaths — `@askrjs/ui/select` and `@askrjs/themes/select` — rather than the package roots so your bundle only pulls in the select-specific code and its dependency chain.
Live examples
The demos on this page compose `Select`, `SelectTrigger`, `SelectValue`, `SelectContent`, `SelectItem`, and friends the same way you will in application code — nothing in the examples reaches into internals. Copy a demo wholesale and swap in your own `SelectItem` values as a starting point.
Anatomy
A full select tree is `Select` (state owner) wrapping a `SelectTrigger` with a `SelectValue` inside it, plus a `SelectPortal` containing `SelectContent`. Inside `SelectContent` you place `SelectItem` elements (each wrapping `SelectItemText`), optionally grouped with `SelectGroup`, `SelectLabel`, and separated with `SelectSeparator`. Every part is a real exported component — there's no implicit slot you have to know about.
State model
`Select` owns two independent pieces of state: the selected `value` and the `open` boolean. Each can run uncontrolled — `value`/`defaultValue`/`onValueChange` for the selection, `open`/`defaultOpen`/`onOpenChange` for the popover — or be handed to you fully controlled when the caller needs to own it. Leave both uncontrolled for a typical form field, or control `value` when the selection needs to sync with data loaded elsewhere. Internally the root also tracks a `currentIndexCandidate` for keyboard highlighting, but that's not something you set from the outside.
Keyboard and accessibility
The trigger carries `role="button"` with `aria-expanded`, `aria-controls`, and `aria-haspopup` wired automatically; the popover content gets `role="listbox"` and each item `role="option"` with `aria-selected` reflecting the current value. These roles come straight from the `SELECT_A11Y_CONTRACT` object the package exports, so you can inspect it directly if you want to assert on the contract in tests rather than guessing at attribute names.
Styling and tokens
`@askrjs/themes/select` styles every part through `data-slot`, `data-state`, `data-disabled`, `data-side`, and `data-align` attributes rather than baking classes into the DOM structure, so you can restyle a specific state (open, disabled, a given popover side) with an attribute selector instead of overriding a component prop. `SelectTrigger` also exposes a `size` prop (`sm` | `md` | `lg`) for the common case of just needing a bigger or smaller control.
API
`Select` accepts `value`, `defaultValue`, `onValueChange`, `open`, `defaultOpen`, `onOpenChange`, `name`, and `disabled`. `SelectItem` requires a `value` string and optionally takes `disabled` and `textValue` (useful when the rendered children aren't plain text but you still need a string for matching). `SelectContent` takes `side`, `align`, `sideOffset`, and `forceMount` for positioning and mount-timing control, matching the same overlay-placement props you'll see on popovers and menus elsewhere in the library.
Edge cases
When `name` is set, `Select` renders a hidden `<input type="hidden">` that mirrors the current value, so it participates in native form submission and `FormData` without you wiring anything up. `disabled` on the root disables the whole control; per-item `disabled` skips just that option during keyboard traversal while leaving the rest of the list navigable.
Related pages
If you only need a handful of mutually exclusive options always visible on screen, `Radio Group` is usually a better fit than `Select`. For the browser-native alternative — useful inside constrained contexts like `<form>` elements targeting non-JS fallback — see the Native Select page under experimental controls.