Components
SelectField
A dropdown that matches Field and InlineField in height and label treatment, so the three interleave in a form grid without any of them riding high.
Web-only, and the dropdown renders through a portal so it escapes any clipping or stacking context the trigger sits inside.
Three modes share one component: single (checkmark, closes on select),
radio (radio circle, closes on select) and multi (checkboxes, stays open,
trigger shows removable badges).
function Single({ initial = 'np', ...props }: { initial?: string } & Record<string, any>) {
const [value, setValue] = React.useState(initial);
return <SelectField options={COUNTRIES} {...props} value={value} onSelect={setValue as any} />;
}
<View style={{ maxWidth: 380 }}>
<Single {...(args as any)} />
</View>Import
import { SelectField } from '@simal/ui/nativewindui';Props
| Prop | Values | Default | Description |
|---|---|---|---|
options | — | [] | Each needs id and label. subtitle adds a second line, disabled blocks selection, and group renders section headers — callers must pre-sort so same-group entries are contiguous. |
label | — | "Country" | Sits above the trigger. Omit it and the trigger stands alone. |
placeholder | — | "Select a country" | Shown on the trigger while nothing is selected. |
mode | single · radio · multi | "single" | multi switches value and onSelect to arrays. The other two differ only in affordance. |
searchable | boolean | false | Adds a search input inside the dropdown that filters by label. Worth it past roughly ten options. |
isLoading | boolean | false | Replaces the trigger with a spinner. For options still being fetched. |
errorText | — | — | Any non-empty string turns the label and border red and renders below the trigger. |
disabled | boolean | — | Blocks opening and dims the trigger. |
value | — | "" | Controlled. A string in single and radio modes, string[] in multi. |
onSelect | — | — | Required. Receives the new id, or the full array in multi mode. |
onCreate | — | — | Enables a "Create …" row when a search returns nothing. Should create the entity and resolve to its new id, or null on failure. |
Examples
States
Closed triggers only — the dropdown is a portal, so an open one would escape the snapshot frame.
function Single({ initial = 'np', ...props }: { initial?: string } & Record<string, any>) {
const [value, setValue] = React.useState(initial);
return <SelectField options={COUNTRIES} {...props} value={value} onSelect={setValue as any} />;
}
<View className="gap-5" style={{ maxWidth: 380 }}>
<Single label="Selected" initial="np" />
<Single label="Empty" initial="" placeholder="Select a country" />
<Single label="With error" initial="" placeholder="Required" errorText="Pick a country." />
<Single label="Disabled" initial="np" disabled />
<Single label="Loading" isLoading />
</View>With Subtitles
Subtitles give a second line for disambiguation — two people with the same name, two units with the same code.
function Single({ initial = 'np', ...props }: { initial?: string } & Record<string, any>) {
const [value, setValue] = React.useState(initial);
return <SelectField options={COUNTRIES} {...props} value={value} onSelect={setValue as any} />;
}
<View style={{ maxWidth: 380 }}>
<Single
label="Assignee"
initial="a"
options={[
{ id: 'a', label: 'Anita Gurung', subtitle: 'Finance' },
{ id: 'b', label: 'Bikash Thapa', subtitle: 'Operations' },
]}
/>
</View>In A Grid
Beside an InlineField, which is the pairing the shared 48px height exists for.
function Single({ initial = 'np', ...props }: { initial?: string } & Record<string, any>) {
const [value, setValue] = React.useState(initial);
return <SelectField options={COUNTRIES} {...props} value={value} onSelect={setValue as any} />;
}
<View className="flex-row gap-5" style={{ maxWidth: 640 }}>
<View style={{ flex: 1 }}><Single label="Country" initial="np" /></View>
<View style={{ flex: 1 }}><Single label="Currency" initial="npr" options={[{ id: 'npr', label: 'NPR' }, { id: 'inr', label: 'INR' }]} /></View>
</View>