Select
A real <select> with the native arrow replaced. Pass <option> elements as
children; value, defaultValue, onChange, multiple and ref all pass
straight through.
Props
| Prop | Values | Default | Description |
|---|---|---|---|
size | "sm", "md", "lg" | "md" | Height, padding, and text size. |
invalid | boolean | false | Applies the danger border and sets aria-invalid. |
className | string | "" | Appended to the <select>, not the wrapper. |
All other <select> attributes are forwarded.
Common patterns
<Select value={role} onChange={(e) => setRole(e.target.value)}>
<option value="admin">Admin</option>
<option value="member">Member</option>
</Select>
// Grouped options work as usual
<Select>
<optgroup label="Staff">
<option>Admin</option>
</optgroup>
</Select>Notes
Staying native is the point. The browser supplies the dropdown, which means it is correct on touch, correct with a screen reader, and has no positioning code that can misplace a menu near the edge of the viewport.
The cost is that you cannot style the options themselves — that surface belongs
to the operating system. If you need styled option rows, you need a listbox
built from <div>s, which is a different and much larger component.
The wrapper <span> exists only to position the arrow. className goes to the
<select>, so sizing and colors work the way you'd expect.