import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";
export default function NativeSelectBasic() {
return (
<NativeSelect aria-label="Status" defaultValue="in-progress">
<NativeSelectOption value="backlog">Backlog</NativeSelectOption>
<NativeSelectOption value="todo">To do</NativeSelectOption>
<NativeSelectOption value="in-progress">In progress</NativeSelectOption>
<NativeSelectOption value="done">Done</NativeSelectOption>
</NativeSelect>
);
}- Add the registryOnce per project
pnpm dlx shadcn@latest registry add @jarvis=https://ui.jarv.is/r/{name}.json - Add native-select
pnpm dlx shadcn@latest add @jarvis/native-select
Usage
import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";
export function SizeSelect() {
return (
<NativeSelect aria-label="Size" defaultValue="m">
<NativeSelectOption value="s">Small</NativeSelectOption>
<NativeSelectOption value="m">Medium</NativeSelectOption>
<NativeSelectOption value="l">Large</NativeSelectOption>
</NativeSelect>
);
}A native select uses the platform's own picker, which works well on phones. Its options can only be text; for anything richer, use a Select.
Composition
Examples
With a label
A FieldLabel in the same Field names the select. Without one, give it an aria-label.
import { Field, FieldLabel } from "@/components/ui/field";
import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";
export default function NativeSelectWithLabel() {
return (
<Field className="w-full max-w-sm">
<FieldLabel htmlFor="region">Region</FieldLabel>
<NativeSelect id="region" defaultValue="us" className="w-full">
<NativeSelectOption value="us">United States</NativeSelectOption>
<NativeSelectOption value="eu">Europe</NativeSelectOption>
<NativeSelectOption value="ap">Asia Pacific</NativeSelectOption>
</NativeSelect>
</Field>
);
}Sizes
import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";
export default function NativeSelectSizes() {
return (
<div className="flex flex-wrap items-center gap-3">
<NativeSelect size="sm" aria-label="Rows per page" defaultValue="25">
<NativeSelectOption value="10">10 rows</NativeSelectOption>
<NativeSelectOption value="25">25 rows</NativeSelectOption>
<NativeSelectOption value="50">50 rows</NativeSelectOption>
</NativeSelect>
<NativeSelect aria-label="Rows per page" defaultValue="25">
<NativeSelectOption value="10">10 rows</NativeSelectOption>
<NativeSelectOption value="25">25 rows</NativeSelectOption>
<NativeSelectOption value="50">50 rows</NativeSelectOption>
</NativeSelect>
<NativeSelect size="lg" aria-label="Rows per page" defaultValue="25">
<NativeSelectOption value="10">10 rows</NativeSelectOption>
<NativeSelectOption value="25">25 rows</NativeSelectOption>
<NativeSelectOption value="50">50 rows</NativeSelectOption>
</NativeSelect>
</div>
);
}Groups
Group options with NativeSelectOptGroup.
import {
NativeSelect,
NativeSelectOptGroup,
NativeSelectOption,
} from "@/components/ui/native-select";
export default function NativeSelectGroups() {
return (
<NativeSelect aria-label="Time zone" defaultValue="">
<NativeSelectOption value="" disabled>
Select a time zone
</NativeSelectOption>
<NativeSelectOptGroup label="Americas">
<NativeSelectOption value="america-los-angeles">Pacific Time</NativeSelectOption>
<NativeSelectOption value="america-denver">Mountain Time</NativeSelectOption>
<NativeSelectOption value="america-chicago">Central Time</NativeSelectOption>
<NativeSelectOption value="america-new-york">Eastern Time</NativeSelectOption>
</NativeSelectOptGroup>
<NativeSelectOptGroup label="Europe">
<NativeSelectOption value="europe-london">London</NativeSelectOption>
<NativeSelectOption value="europe-berlin">Berlin</NativeSelectOption>
<NativeSelectOption value="europe-helsinki">Helsinki</NativeSelectOption>
</NativeSelectOptGroup>
<NativeSelectOptGroup label="Asia Pacific">
<NativeSelectOption value="asia-singapore">Singapore</NativeSelectOption>
<NativeSelectOption value="asia-tokyo">Tokyo</NativeSelectOption>
<NativeSelectOption value="australia-sydney">Sydney</NativeSelectOption>
</NativeSelectOptGroup>
</NativeSelect>
);
}Disabled
import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";
export default function NativeSelectDisabled() {
return (
<NativeSelect aria-label="Region" disabled>
<NativeSelectOption value="us">United States</NativeSelectOption>
</NativeSelect>
);
}Invalid
Set invalid on the Field and explain the problem with a FieldError.
Choose a country.
import { Field, FieldError, FieldLabel } from "@/components/ui/field";
import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";
export default function NativeSelectInvalid() {
return (
<Field invalid className="w-fit">
<FieldLabel>Country</FieldLabel>
<NativeSelect defaultValue="">
<NativeSelectOption value="" disabled>
Select a country
</NativeSelectOption>
<NativeSelectOption value="ca">Canada</NativeSelectOption>
<NativeSelectOption value="de">Germany</NativeSelectOption>
<NativeSelectOption value="jp">Japan</NativeSelectOption>
</NativeSelect>
<FieldError>Choose a country.</FieldError>
</Field>
);
}Props
className goes to the wrapper <div>, so set the width there.
| Part | Extends |
|---|---|
| NativeSelect | <select> |
| NativeSelectOption | <option> |
| NativeSelectOptGroup | <optgroup> |
The tables leave out className, style and render. Parts built on Base UI take them as its styling and composition guides describe.
NativeSelect
| Prop | Type | Default |
|---|---|---|
size | "default" | "lg" | "sm" | "default" |