SelectInput
A single-selection list with radio indicators and groups
A single-selection list with radio indicators, keyboard navigation, group headers, and a submitted state.
Run demo
bunx @gridland/demo select-inputcurl -fsSL https://raw.githubusercontent.com/thoughtfulllc/gridland/main/scripts/run-demo.sh | bash -s select-inputInstallation
bunx create-gridland add select-inputUsage
import { SelectInput } from "@/components/ui/select-input"<SelectInput
focusId="language"
autoFocus
items={[
{ label: "TypeScript", value: "ts" },
{ label: "JavaScript", value: "js" },
{ label: "Python", value: "py" },
]}
title="Select a language"
onSubmit={(value) => console.log("Selected:", value)}
/>SelectInput registers with the focus system via focusId. Wrap your app in
GridlandProvider (which implicitly mounts <FocusProvider selectable>) or a
standalone <FocusProvider selectable> for keyboard navigation to work. Tab to
focus, Enter to select (start interacting), then use arrow keys. Escape deselects.
Examples
Controlled
Use value and onChange to control the selection externally.
const [value, setValue] = useState<string>("ts")
<SelectInput
focusId="language"
autoFocus
items={[
{ label: "TypeScript", value: "ts" },
{ label: "JavaScript", value: "js" },
{ label: "Python", value: "py" },
]}
value={value}
onChange={setValue}
title="Select a language"
onSubmit={(value) => console.log("Selected:", value)}
/>Default Value
Set the initially selected item in uncontrolled mode.
<SelectInput
focusId="language"
autoFocus
items={[
{ label: "TypeScript", value: "ts" },
{ label: "JavaScript", value: "js" },
{ label: "Python", value: "py" },
]}
defaultValue="ts"
title="Select a language"
onSubmit={(value) => console.log("Selected:", value)}
/>Groups
Use the group field on items to render group headers with separators.
<SelectInput
focusId="tool"
autoFocus
items={[
{ label: "TypeScript", value: "ts", group: "Languages" },
{ label: "Python", value: "py", group: "Languages" },
{ label: "React", value: "react", group: "Frameworks" },
{ label: "Vue", value: "vue", group: "Frameworks" },
]}
title="Select a tool"
onSubmit={(value) => console.log(value)}
/>Disabled Items
Disable individual items so they cannot be selected.
<SelectInput
focusId="language"
autoFocus
items={[
{ label: "TypeScript", value: "ts" },
{ label: "JavaScript", value: "js", disabled: true },
{ label: "Python", value: "py" },
]}
title="Select a language"
onSubmit={(value) => console.log(value)}
/>Required
Show a required indicator and use invalid to display an error state.
<SelectInput
focusId="language"
autoFocus
items={[
{ label: "TypeScript", value: "ts" },
{ label: "JavaScript", value: "js" },
{ label: "Python", value: "py" },
]}
required
invalid={!hasSelected}
title="Select a language"
onSubmit={(value) => console.log(value)}
/>Disabled
Disable the entire component. Navigation and submission are blocked.
<SelectInput
focusId="language"
items={[
{ label: "TypeScript", value: "ts" },
{ label: "JavaScript", value: "js" },
]}
disabled
title="Select a language"
/>Placeholder
Show placeholder text when the items list is empty.
<SelectInput
focusId="language"
autoFocus
items={[]}
placeholder="No options available"
title="Select a language"
/>Controls
- ↑/↓ or j/k: Navigate and select
- Enter: Submit selected item
API Reference
SelectInput
| Prop | Type | Default | Description |
|---|---|---|---|
items | SelectInputItem<V>[] | [] | Array of selectable items |
defaultValue | V | - | Initially selected value (uncontrolled) |
value | V | - | Selected value (controlled) |
onChange | (value: V) => void | - | Called when selection changes |
disabled | boolean | false | Disable the entire component |
invalid | boolean | false | Show error state with destructive styling |
errorMessage | string | "Please select an option" | Custom error message when invalid |
required | boolean | false | Show required indicator (*) next to title |
placeholder | string | - | Placeholder text when items list is empty |
title | string | "Select" | Title shown next to the diamond indicator |
submittedStatus | string | "submitted" | Status text shown after submit |
limit | number | 12 | Max visible rows before scrolling |
highlightColor | string | theme.primary | Color of the highlighted item |
radioColor | string | theme.muted | Color of the radio indicator |
onSubmit | (value: V) => void | - | Called on Enter with the selected value |
focusId | string | auto-generated | Stable id for the focus system |
autoFocus | boolean | false | Focus this component on mount |
SelectInputItem
| Field | Type | Description |
|---|---|---|
label | string | Display text |
value | V | Item value |
key | string? | Optional React key |
group | string? | Group header label |
disabled | boolean? | Disable individual item |