Native Select
Browser-native selection with Migoy control styling and standard form behavior.
Preview
Use Native Select for straightforward choices when native browser behavior and mobile pickers are useful. Use Select for a custom popup with consistent menu styling. Neither provides searchable selection.
Opened when you sign in.
Installation
pnpm dlx shadcn@latest add http://127.0.0.1:3017/r/native-select.jsonInstalls NativeSelect, NativeSelectOption, and NativeSelectOptGroup in one source file, plus Lucide for the decorative chevron. No Radix dependency is required. Uses your existing cn utility and semantic colors; Field and the optional theme are separate items. This is a local development snapshot.
Usage
import {
NativeSelect, NativeSelectOption, NativeSelectOptGroup,
} from '@/components/ui/native-select';These components render a native select, option, and optgroup. Standard select attributes and ref reach the actual control. Put a visible label above it and match htmlFor with its id. The wrapper fills its parent; constrain the Field or another layout container for shorter controls. className styles the select itself.
For controlled usage, use value with onChange and read event.target.value. For uncontrolled usage, use defaultValue. Do not use selected on individual options. Group related choices with NativeSelectOptGroup's label prop; individual options and whole groups can be disabled.
Composition
Native Select uses native HTML relationships rather than a separate trigger and popup:
Field
├── FieldLabel
└── NativeSelect
├── NativeSelectOption
└── NativeSelectOptGroup (optional)
└── NativeSelectOption (repeat for each grouped option)Match FieldLabel's htmlFor to NativeSelect's id. Options can be direct children or belong to a labeled NativeSelectOptGroup. Put selection and form attributes on NativeSelect; its wrapper and decorative chevron are internal. The browser provides the open picker.
Sizes
Opened when you sign in.
Opened when you sign in.
Opened when you sign in.
controlSize | Height | Radius | Text |
|---|---|---|---|
sm | 24px | 3px | 12px / 16px |
default | 32px | 4px | 14px / 20px |
lg | 44px | 6px | 14px / 20px |
<NativeSelect controlSize="sm" aria-label="Workspace" defaultValue="design">
<NativeSelectOption value="design">Design</NativeSelectOption>
<NativeSelectOption value="engineering">Engineering</NativeSelectOption>
</NativeSelect>Visual sizing uses controlSize; native size stays numeric and controls the visible list length. Use the large control size for touch-oriented forms. Closed controls follow the same shadow-free surface, hover border, and solid 2px offset focus outline as Input. Shared geometry tokens include fallbacks for installations without the Migoy theme.
Disabled and invalid
Opened when you sign in.
Opened when you sign in.
Apply disabled to NativeSelect. For custom validation, set data-invalid on Field and aria-invalid on NativeSelect; connect the visible FieldError with aria-describedby. Labels remain neutral. aria-invalid communicates your custom validation result; it does not itself prevent form submission.
<Field data-invalid>
<FieldLabel htmlFor="required-workspace">Workspace</FieldLabel>
<NativeSelect id="required-workspace" defaultValue="" aria-invalid aria-describedby="workspace-error">
<NativeSelectOption value="" disabled>Choose a workspace</NativeSelectOption>
<NativeSelectOption value="design">Design</NativeSelectOption>
</NativeSelect>
<FieldError id="workspace-error">Choose a workspace.</FieldError>
</Field>Import Field, FieldLabel, and FieldError from your installed Field module for this example.
Native forms
Use an empty-value option as the initial prompt and add required for native validation. Include name to submit the selected value. Disabled controls are omitted from FormData. Reset restores the initial uncontrolled value. Always validate submitted values and permissions on the server as well.
<NativeSelect name="workspace" defaultValue="" required aria-label="Workspace">
<NativeSelectOption value="" disabled>Choose a workspace</NativeSelectOption>
<NativeSelectOption value="design">Design</NativeSelectOption>
<NativeSelectOption value="engineering">Engineering</NativeSelectOption>
</NativeSelect>Put the control inside a form or associate it with an external form using the native form attribute. The example above uses an accessible name for brevity; prefer a visible label in a product.
Multiple selection and list boxes
Native multiple and numeric size remain supported. List boxes keep their native height based on visible rows and omit the decorative dropdown chevron. controlSize still controls text, padding, and corners. Browser and assistive-technology interaction varies; provide visible instructions and consider a dedicated multi-select pattern for complex workflows.
Use your platform’s modifier keys to select multiple options. Native interaction varies by device.
Read multiple values with formData.getAll('workspaces'), or use event.target.selectedOptions in a controlled component. Object.fromEntries(formData) retains only one entry per repeated field name.
Accessibility and appearance
Keyboard interaction, the open picker, option highlighting, and native validation messages belong to the browser and operating system. The closed select uses Migoy styling; the picker is not the custom Radix Select menu. Option colors use system Canvas/CanvasText colors where supported. In forced-colors mode, the collapsed control restores the native arrow. Avoid promising pixel-identical open menus across platforms.
Based on shadcn Native Select and the React select reference.