NativeSelect
The browser's own select, styled like the Select trigger. Server-safe with no client code; use it where the native picker is the better choice, such as on phones or in Server Component forms.
Import
import { NativeSelect } from '@nizivo/react/native-select';
// Once per app (Getting started), after
// @nizivo/tokens/tokens.css and @nizivo/react/css/base.css
import '@nizivo/react/css/native-select.css';Placed directly inside a Field, or through a function child, a NativeSelect is wired as plain attributes with no client code. It keeps the browser's own validation bubble rather than the Field's inline message, so do not mix it with Field-wired client controls in one native-validated form (see Field).
Static or interactive
Server. It ships no client JavaScript when a Server Component renders it, in every Next.js build
mode; its code is bundled only when a client component (a 'use client' file) imports it. It is the
static alternative to Select when the browser's own picker fits. Inside a Field (placed directly
or through a function child) it is wired with no client code, but Turbopack and next dev still
ship the Field's FieldScope module; FieldStatic ships none. How to
choose.
In a client app (a Vite or React Router single-page app, or a 'use client' file) it is an ordinary React component: event handlers, refs and state work as usual. In a client app.
Text size on touch screens
Under a coarse pointer (a phone or a tablet), the value is 16 px (Label/L) instead of 14 px (Label/M)
at every size, as on the Select trigger, because iOS Safari zooms the page into a focused <select>
whose text is smaller than 16 px. The box keeps its 32, 40 or 48 px height.
--nz-native-select-font replaces the font on every pointer, so a page that sets it also decides
whether iOS zooms.
Examples
Basic
Zero JS: placed in a Field, the select gets its id, name and ARIA wiring as plain attributes (a function child, {(control) => <NativeSelect {...control} />}, does the same). The page ships no client code for it, and the browser validates required itself.
import type { JSX } from 'react';
import { Field } from '@nizivo/react/field';
import { NativeSelect } from '@nizivo/react/native-select';
export function NativeSelectExample(): JSX.Element {
return (
<Field label="Country" description="Where the order ships." required>
<NativeSelect name="country" placeholder="Choose a country">
<option value="IN">India</option>
<option value="GB">United Kingdom</option>
<option value="US">United States</option>
</NativeSelect>
</Field>
);
}
Groups
Option groups, as a native <select> draws them.
import type { JSX } from 'react';
import { NativeSelect } from '@nizivo/react/native-select';
export function NativeSelectGroupsExample(): JSX.Element {
return (
<NativeSelect aria-label="Period" defaultValue="30d">
<optgroup label="Recent">
<option value="7d">Last 7 days</option>
<option value="30d">Last 30 days</option>
</optgroup>
<optgroup label="Longer">
<option value="12m">Last 12 months</option>
<option value="all">All time</option>
</optgroup>
</NativeSelect>
);
}
Sizes
32, 40 and 48 px: the Select trigger's heights, so the two can swap without a layout shift.
import type { JSX } from 'react';
import { NativeSelect } from '@nizivo/react/native-select';
export function NativeSelectSizesExample(): JSX.Element {
return (
<div style={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 12 }}>
<NativeSelect size="sm" aria-label="Small" defaultValue="a">
<option value="a">Small</option>
</NativeSelect>
<NativeSelect aria-label="Medium" defaultValue="a">
<option value="a">Medium</option>
</NativeSelect>
<NativeSelect size="lg" aria-label="Large" defaultValue="a">
<option value="a">Large</option>
</NativeSelect>
</div>
);
}
States
An error from the server, and disabled.
import type { JSX } from 'react';
import { Field } from '@nizivo/react/field';
import { NativeSelect } from '@nizivo/react/native-select';
export function NativeSelectStatesExample(): JSX.Element {
return (
<div style={{ display: 'grid', gap: 16, maxInlineSize: 320 }}>
<Field label="Shipping zone" error="Choose a zone that ships to this address.">
{(control) => (
<NativeSelect {...control} defaultValue="eu">
<option value="eu">Europe</option>
<option value="asia">Asia</option>
</NativeSelect>
)}
</Field>
<Field label="Currency" disabled>
{(control) => (
<NativeSelect {...control} defaultValue="INR">
<option value="INR">Indian rupee</option>
</NativeSelect>
)}
</Field>
</div>
);
}
Props
Server component Renders data-slot="native-select" with the class nz-native-select. Props: NativeSelectProps. Source: src/native-select/native-select.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
children | ReactNode | – |
|
indicator | ReactNode | – | Replaces the chevron glyph. Decorative. |
invalid | boolean | – | An error message is presented for this select (§8.3). A Field's function child passes |
leadingIcon | ReactNode | – | Decorative icon before the value ( |
placeholder | string | – | Text of a first, empty option shown until a value is chosen ( |
readOnly | boolean | – |
|
render | RenderProp<NativeSelectState> | – | Composition of the wrapper. |
size | ControlSize"sm""md""lg" | 'md' | 32 / 40 / 48 px, the Select trigger's heights. |
- Also takes every attribute of
<select>exceptmultiple,readOnly,size;childrenis redeclared above. (Omit<ComponentPropsWithRef<"select">, "size" | "multiple" | "readOnly">)
render takes an element, or, in client code, a function of the props and this state (NativeSelectState):
| Name | Type | Default | Description |
|---|---|---|---|
disabledrequired | boolean | – | |
invalidrequired | boolean | – | An error message is presented: the |
sizerequired | ControlSize"sm""md""lg" | – |
Other exports
NativeSelectPropstypeimport type { NativeSelectProps } from '@nizivo/react/native-select'The props of
NativeSelect, listed above.NativeSelectStatetypeimport type { NativeSelectState } from '@nizivo/react/native-select'The render state of
NativeSelect, listed above.
Data attributes and slots
Style parts by class (.nz-<slot>) and state by data attribute. Attribute meanings come from contracts/vocabulary.json.
| Slot | Rendered by | Data attributes |
|---|---|---|
native-select.nz-native-select | NativeSelectroot (takes render) |
|
native-select-chevron.nz-native-select-chevron | NativeSelectinner element (CSS only) | none |
native-select-icon.nz-native-select-icon | NativeSelectinner element (CSS only) | none |
native-select-trigger.nz-native-select-trigger | NativeSelectinner element (CSS only); interactive, focus: self | none |
CSS hooks
Set a hook on the component's class or any ancestor to restyle it without specificity games; leave it unset for the default shown.
| Hook | Default | Read as |
|---|---|---|
--nz-native-select-bg |
|
|
--nz-native-select-border |
|
|
--nz-native-select-font |
|
|
--nz-native-select-height |
|
|
--nz-native-select-icon-fg | --nz-text-secondary |
|
--nz-native-select-radius |
|
|
Layers: nz.components. Classes: .nz-native-select, .nz-native-select-chevron, .nz-native-select-icon, .nz-native-select-trigger.
Tokens this stylesheet reads (26)
--nz-bg-disabled, --nz-bg-surface, --nz-border-control, --nz-border-control-hover, --nz-border-critical, --nz-border-subtle, --nz-border-width-default, --nz-border-width-focus, --nz-duration-fast, --nz-easing-standard, --nz-focus-halo, --nz-focus-halo-critical, --nz-font-label-l, --nz-font-label-m, --nz-radius-md, --nz-radius-sm, --nz-size-control-lg, --nz-size-control-md, --nz-size-control-sm, --nz-size-icon-sm, --nz-space-2, --nz-space-3, --nz-text-disabled, --nz-text-primary, --nz-text-secondary, --nz-text-tertiary
Accessibility
These parts are interactive. Each gets the focus outline and touch-action rule from @nizivo/react/css/base.css (the nz.base layer), so the floor survives a restyle:
native-select-trigger: draws the focus outline itself on keyboard focus.
Keyboard
| Keys | Action |
|---|---|
| Tab | Moves focus to the select. |
| ArrowUp, ArrowDown, Space, Enter, Type a character | The browser's own select keyboard, which differs by platform; typing jumps to the next option that starts with what you typed. |
Source: a native <select> (native-select.tsx), which adds no key handling.
Figma
Node ids from design/component-map.json, which nz gen writes from component.json. The links open the Nizivo Figma library, which may ask you to sign in.
- Canonical: none
- Ghost masters: none
Motion
CountUp: a number that counts up to its value on the client, while the server renders the final value in NumberValue's element. Tabular digits that keep their width, 0 ms under reduced motion, and nothing announced while it counts. Also useCountUp, for an element of your own.
NavItem
One sidebar destination, as a native link with an icon, a label and an optional count. The current one is aria-current="page"; a router's link goes in through render. In a Sidebar's icon rail it folds to its icon, with no prop; SidebarItem adds the rail's Tooltip.