Skip to content
UI

Native Select

The browser's own select element, styled like the other form controls, for picking one option from a list.

@zenoxai/ui/components/native-select Markdown
<NativeSelect defaultValue="high" aria-label="Minimum severity">
  <NativeSelectOption value="critical">Critical</NativeSelectOption>
  <NativeSelectOption value="high">High and above</NativeSelectOption>
  <NativeSelectOption value="medium">Medium and above</NativeSelectOption>
  <NativeSelectOption value="low">All severities</NativeSelectOption>
</NativeSelect>

Usage

import {
  NativeSelect,
  NativeSelectOption,
} from "@zenoxai/ui/components/native-select"

<NativeSelect defaultValue="weekly">
  <NativeSelectOption value="daily">Daily</NativeSelectOption>
  <NativeSelectOption value="weekly">Weekly</NativeSelectOption>
</NativeSelect>

NativeSelect renders a real <select>, so the option list is the operating system’s: a wheel on iOS, a sheet on Android, a plain menu on the desktop. It submits with a form, works before JavaScript loads and needs no portal.

Choose it over Select on touch-first screens, in long server-rendered forms, and when the options are plain text. Choose Select when options need icons, descriptions or the platform’s popup styling, and Combobox when the user needs to search the list.

All <select> attributes pass through to the element: value, onChange, name, required, disabled, aria-*. className goes to the wrapper <div>, which also holds the chevron icon.

Examples

With a label

Wrap it in a Field and point the FieldLabel at the select’s id. In a vertical field the select fills the width.

Reports are sent on Monday at 08:00 UTC.

<Field className="max-w-xs">
  <FieldLabel htmlFor="report-frequency">Report frequency</FieldLabel>
  <NativeSelect id="report-frequency" defaultValue="weekly">
    <NativeSelectOption value="daily">Daily</NativeSelectOption>
    <NativeSelectOption value="weekly">Weekly</NativeSelectOption>
    <NativeSelectOption value="monthly">Monthly</NativeSelectOption>
  </NativeSelect>
  <FieldDescription>
    Reports are sent on Monday at 08:00 UTC.
  </FieldDescription>
</Field>

Groups

NativeSelectOptGroup renders an <optgroup> with a label.

<NativeSelect defaultValue="7d" aria-label="Time range">
  <NativeSelectOptGroup label="Relative">
    <NativeSelectOption value="24h">Last 24 hours</NativeSelectOption>
    <NativeSelectOption value="7d">Last 7 days</NativeSelectOption>
    <NativeSelectOption value="30d">Last 30 days</NativeSelectOption>
  </NativeSelectOptGroup>
  <NativeSelectOptGroup label="Calendar">
    <NativeSelectOption value="this-month">This month</NativeSelectOption>
    <NativeSelectOption value="last-month">Last month</NativeSelectOption>
    <NativeSelectOption value="this-quarter">
      This quarter
    </NativeSelectOption>
  </NativeSelectOptGroup>
</NativeSelect>

Sizes

size="sm" (28px) fits toolbars and filter bars, default (32px) is the form size and lg (36px) matches a large Input or Button. The chevron is 14px at sm and 16px otherwise. The native size attribute (number of visible rows) is not available. Below the md breakpoint the text is 16px, as in Input, so iOS does not zoom the page when the select takes focus.

<>
  <NativeSelect size="sm" defaultValue="open" aria-label="Status (small)">
    <NativeSelectOption value="open">Open</NativeSelectOption>
    <NativeSelectOption value="closed">Closed</NativeSelectOption>
  </NativeSelect>
  <NativeSelect defaultValue="open" aria-label="Status">
    <NativeSelectOption value="open">Open</NativeSelectOption>
    <NativeSelectOption value="closed">Closed</NativeSelectOption>
  </NativeSelect>
  <NativeSelect size="lg" defaultValue="open" aria-label="Status (large)">
    <NativeSelectOption value="open">Open</NativeSelectOption>
    <NativeSelectOption value="closed">Closed</NativeSelectOption>
  </NativeSelect>
</>

Disabled and invalid

Set disabled on the select and data-disabled on the Field. For an error, set aria-invalid on the select and data-invalid on the Field. A first option with an empty value and disabled works as a placeholder that the user cannot pick back.

<div className="grid w-full max-w-xs gap-4">
  <Field data-disabled>
    <FieldLabel htmlFor="data-region">Data region</FieldLabel>
    <NativeSelect id="data-region" defaultValue="eu" disabled>
      <NativeSelectOption value="eu">Europe (Frankfurt)</NativeSelectOption>
      <NativeSelectOption value="us">
        United States (Virginia)
      </NativeSelectOption>
    </NativeSelect>
  </Field>
  <Field data-invalid>
    <FieldLabel htmlFor="escalation-owner">Escalation owner</FieldLabel>
    <NativeSelect
      id="escalation-owner"
      defaultValue=""
      aria-invalid
      aria-describedby="escalation-owner-error"
    >
      <NativeSelectOption value="" disabled>
        Select a team
      </NativeSelectOption>
      <NativeSelectOption value="soc">SOC tier 1</NativeSelectOption>
      <NativeSelectOption value="ir">Incident response</NativeSelectOption>
    </NativeSelect>
    <FieldError id="escalation-owner-error">
      Select the team that handles escalations.
    </FieldError>
  </Field>
</div>

Accessibility

  • Keyboard, screen reader and touch behavior are the browser’s, so they match what users know from every other site.
  • Name the select with a FieldLabel and htmlFor, or with aria-label when there is no visible label.
  • The chevron is decorative and hidden from assistive technology.

API reference

Generated from the TypeScript source. className, style and children are accepted everywhere and omitted below.

NativeSelect

NativeSelect is the browser's own <select>, styled like Input, with NativeSelectOption and NativeSelectOptGroup inside. It suits mobile forms, where the system picker is easier to use; for a styled popup with richer items, use Select.

PropTypeDefault
size"default" | "sm" | "lg"

Height: `sm` 28px, `default` 32px, `lg` 36px, the heights of Input and Button. The chevron is 14px at `sm`, 16px otherwise.

default

NativeSelectOptGroup

NativeSelectOptGroup groups options under a label, as an <optgroup>.

No props of its own. Accepts the props of the element it renders.

NativeSelectOption

NativeSelectOption is one <option> of a NativeSelect.

No props of its own. Accepts the props of the element it renders.