8.6k

Native Select

PreviousNext

A styled native HTML select element with consistent design system integration.

<script setup lang="ts">
import {
  NativeSelect,
  NativeSelectOption,
} from '@/registry/new-york-v4/ui/native-select'
</script>

<template>
  <NativeSelect>
    <NativeSelectOption value="">
      Select status
    </NativeSelectOption>
    <NativeSelectOption value="todo">
      Todo
    </NativeSelectOption>
    <NativeSelectOption value="in-progress">
      In Progress
    </NativeSelectOption>
    <NativeSelectOption value="done">
      Done
    </NativeSelectOption>
    <NativeSelectOption value="cancelled">
      Cancelled
    </NativeSelectOption>
  </NativeSelect>
</template>

Installation

pnpm dlx shadcn-vue@latest add native-select

Usage

<script setup lang="ts">
import {
  NativeSelect,
  NativeSelectOptGroup,
  NativeSelectOption,
} from "@/components/ui/native-select"
</script>

<template>
  <NativeSelect>
    <NativeSelectOption value="">Select a fruit</NativeSelectOption>
    <NativeSelectOption value="apple">Apple</NativeSelectOption>
    <NativeSelectOption value="banana">Banana</NativeSelectOption>
    <NativeSelectOption value="blueberry">Blueberry</NativeSelectOption>
    <NativeSelectOption value="grapes" disabled>
      Grapes
    </NativeSelectOption>
    <NativeSelectOption value="pineapple">Pineapple</NativeSelectOption>
  </NativeSelect>
</template>

Examples

With Groups

Organize options using NativeSelectOptGroup for better categorization.

<script setup lang="ts">
import {
  NativeSelect,
  NativeSelectOptGroup,
  NativeSelectOption,
} from '@/registry/new-york-v4/ui/native-select'
</script>

<template>
  <NativeSelect>
    <NativeSelectOption value="">
      Select department
    </NativeSelectOption>
    <NativeSelectOptGroup label="Engineering">
      <NativeSelectOption value="frontend">
        Frontend
      </NativeSelectOption>
      <NativeSelectOption value="backend">
        Backend
      </NativeSelectOption>
      <NativeSelectOption value="devops">
        DevOps
      </NativeSelectOption>
    </NativeSelectOptGroup>
    <NativeSelectOptGroup label="Sales">
      <NativeSelectOption value="sales-rep">
        Sales Rep
      </NativeSelectOption>
      <NativeSelectOption value="account-manager">
        Account Manager
      </NativeSelectOption>
      <NativeSelectOption value="sales-director">
        Sales Director
      </NativeSelectOption>
    </NativeSelectOptGroup>
    <NativeSelectOptGroup label="Operations">
      <NativeSelectOption value="support">
        Customer Support
      </NativeSelectOption>
      <NativeSelectOption value="product-manager">
        Product Manager
      </NativeSelectOption>
      <NativeSelectOption value="ops-manager">
        Operations Manager
      </NativeSelectOption>
    </NativeSelectOptGroup>
  </NativeSelect>
</template>
<template>
  <NativeSelect>
    <NativeSelectOption value="">Select a food</NativeSelectOption>
    <NativeSelectOptGroup label="Fruits">
      <NativeSelectOption value="apple">Apple</NativeSelectOption>
      <NativeSelectOption value="banana">Banana</NativeSelectOption>
      <NativeSelectOption value="blueberry">Blueberry</NativeSelectOption>
    </NativeSelectOptGroup>
    <NativeSelectOptGroup label="Vegetables">
      <NativeSelectOption value="carrot">Carrot</NativeSelectOption>
      <NativeSelectOption value="broccoli">Broccoli</NativeSelectOption>
      <NativeSelectOption value="spinach">Spinach</NativeSelectOption>
    </NativeSelectOptGroup>
  </NativeSelect>
</template>

Disabled State

Disable individual options or the entire select component.

<script setup lang="ts">
import {
  NativeSelect,
  NativeSelectOption,
} from '@/registry/new-york-v4/ui/native-select'
</script>

<template>
  <NativeSelect disabled>
    <NativeSelectOption value="">
      Select priority
    </NativeSelectOption>
    <NativeSelectOption value="low">
      Low
    </NativeSelectOption>
    <NativeSelectOption value="medium">
      Medium
    </NativeSelectOption>
    <NativeSelectOption value="high">
      High
    </NativeSelectOption>
    <NativeSelectOption value="critical">
      Critical
    </NativeSelectOption>
  </NativeSelect>
</template>

Invalid State

Show validation errors with the aria-invalid attribute and error styling.

<script setup lang="ts">
import {
  NativeSelect,
  NativeSelectOption,
} from '@/registry/new-york-v4/ui/native-select'
</script>

<template>
  <NativeSelect aria-invalid="true">
    <NativeSelectOption value="">
      Select role
    </NativeSelectOption>
    <NativeSelectOption value="admin">
      Admin
    </NativeSelectOption>
    <NativeSelectOption value="editor">
      Editor
    </NativeSelectOption>
    <NativeSelectOption value="viewer">
      Viewer
    </NativeSelectOption>
    <NativeSelectOption value="guest">
      Guest
    </NativeSelectOption>
  </NativeSelect>
</template>
<template>
  <NativeSelect aria-invalid="true">
    <NativeSelectOption value="">Select a country</NativeSelectOption>
    <NativeSelectOption value="us">United States</NativeSelectOption>
    <NativeSelectOption value="uk">United Kingdom</NativeSelectOption>
    <NativeSelectOption value="ca">Canada</NativeSelectOption>
  </NativeSelect>
</template>

Form Integration

Use with form libraries like VeeValidate for controlled components.

Select a country

<script setup lang="ts">
import { toTypedSchema } from '@vee-validate/zod'
import { useForm } from 'vee-validate'
import { toast } from 'vue-sonner'

import { z } from 'zod'
import { Button } from '@/registry/new-york-v4/ui/button'
import {
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from '@/registry/new-york-v4/ui/form'
import {
  NativeSelect,
  NativeSelectOption,
} from '@/registry/new-york-v4/ui/native-select'

const formSchema = toTypedSchema(z.object({
  country: z.string().min(2, {
    message: 'Select a country.',
  }),
}))

const form = useForm({
  validationSchema: formSchema,
})

const onSubmit = form.handleSubmit((values) => {
  console.log('Form submitted!', values)
  toast('You submitted the following values:', {
    description: h('pre', { class: 'mt-2 rounded-md bg-neutral-900 p-4 w-full' }, h('code', { class: 'text-code-foreground' }, JSON.stringify(values, null, 2))),
  })
})
</script>

<template>
  <form class="w-full max-w-sm space-y-6" @submit="onSubmit">
    <FormField v-slot="{ field }" name="country">
      <FormItem>
        <FormLabel>Country</FormLabel>
        <FormControl>
          <NativeSelect v-bind="field">
            <NativeSelectOption value="">
              Select a country
            </NativeSelectOption>
            <NativeSelectOption value="us">
              United States
            </NativeSelectOption>
            <NativeSelectOption value="uk">
              United Kingdom
            </NativeSelectOption>
            <NativeSelectOption value="ca">
              Canada
            </NativeSelectOption>
          </NativeSelect>
        </FormControl>
        <FormDescription>
          Select a country
        </FormDescription>
        <FormMessage />
      </FormItem>
    </FormField>

    <Button type="submit" class="w-full">
      Submit
    </Button>
  </form>
</template>

Native Select vs Select

  • Use NativeSelect when you need native browser behavior, better performance, or mobile-optimized dropdowns.
  • Use Select when you need custom styling, animations, or complex interactions.

The NativeSelect component provides native HTML select functionality with consistent styling that matches your design system.

Accessibility

  • The component maintains all native HTML select accessibility features.
  • Screen readers can navigate through options using arrow keys.
  • The chevron icon is marked as aria-hidden="true" to avoid duplication.
  • Use aria-label or aria-labelledby for additional context when needed.
<template>
  <NativeSelect aria-label="Choose your preferred language">
    <NativeSelectOption value="en">English</NativeSelectOption>
    <NativeSelectOption value="es">Spanish</NativeSelectOption>
    <NativeSelectOption value="fr">French</NativeSelectOption>
  </NativeSelect>
</template>

API Reference

NativeSelect

The main select component that wraps the native HTML select element.

PropTypeDefault
classstring

All other props are passed through to the underlying <select> element.

<NativeSelect>
  <NativeSelectOption value="option1">Option 1</NativeSelectOption>
  <NativeSelectOption value="option2">Option 2</NativeSelectOption>
</NativeSelect>

NativeSelectOption

Represents an individual option within the select.

PropTypeDefault
valuestring
disabledbooleanfalse
classstring

All other props are passed through to the underlying <option> element.

<template>
  <NativeSelectOption value="apple">Apple</NativeSelectOption>
  <NativeSelectOption value="banana" disabled>
    Banana
  </NativeSelectOption>
</template>

NativeSelectOptGroup

Groups related options together for better organization.

PropTypeDefault
labelstring
disabledbooleanfalse
classstring

All other props are passed through to the underlying <optgroup> element.

<template>
  <NativeSelectOptGroup label="Fruits">
    <NativeSelectOption value="apple">Apple</NativeSelectOption>
    <NativeSelectOption value="banana">Banana</NativeSelectOption>
  </NativeSelectOptGroup>
</template>