Select lets users choose one option from a list, styled to match the filled Text Field
trigger. It's powered by bits-ui's Select.Root and is
entirely driven by an options array — it does not accept option
elements as children.
Material Design 3 doesn't have a standalone top-level page for "Select" — dropdown
selection is specified as part of the Menus family. This component pairs a Text-Field-style trigger with a menu-style Select.Content popover.
Pass a flat array of { value, label } items to options, or nest them under { type: "group", heading, items } entries for labelled sections.
import { Select } from '@noxlovette/material'; See it live, with Controls, in Storybook rather than a hand-rolled preview here.
Playground
Open the Select story, plus Grouped Options and States variants.
Bind value and pass an options array:
<script lang="ts">
import { Select } from '@noxlovette/material';
import type { SelectOption } from '@noxlovette/material';
let fruit = $state<string | undefined>();
const options: SelectOption[] = [
{ value: 'apple', label: 'Apple' },
{ value: 'banana', label: 'Banana' },
{ value: 'cherry', label: 'Cherry' }
];
</script>
<Select type="single" placeholder="Choose a fruit" {options} bind:value={fruit} /> | Prop | Type | Default | Description |
|---|---|---|---|
type required | "single" | "multiple" | — | Selection mode, from bits-ui's Select.RootProps — 'single' binds value to a string, 'multiple' to a string[]. |
value | string | string[] | undefined | — | The selected value(s), matching bits-ui Select.RootProps. Bindable. |
open | boolean | false | Whether the option list is open. Bindable. |
options | SelectOption[] | [] | Flat or grouped list of options; drives the entire dropdown content (see below). |
placeholder required | string | — | Text shown in the trigger when no value is selected. |
disabled | boolean | false | Disables the select. |
error | boolean | false | Renders the select in an error state. |
supportingText | Snippet | — | Helper text displayed below the select. |
leadingIconProps | IconProps | — | Props for an optional leading icon in the trigger. |
triggerProps | Select.TriggerProps | — | Additional props forwarded to bits-ui's Select.Trigger. |
contentProps | Select.ContentProps | — | Additional props forwarded to bits-ui's Select.Content. |
portalDisabled | boolean | false | Disables the portal — useful when the select is nested inside a Dialog/Sheet that manages its own stacking. |
Each entry in options is either an item or a group:
| Field | Type | Default | Description |
|---|---|---|---|
value required | string | — | The option value. |
label | string | — | Display label; also used as the accessible name. |
disabled | boolean | false | Disables this individual option. |
type | "item" | "group" | "item" | Set to 'group' with a heading and nested items to render a labelled group instead of a single option. |
When type: 'group' is set, use heading (optional) and a nested items: SelectOption[] array instead of value/label.
SelectItem, SelectGroup, SelectLabel, and SelectSeparator are also exported. They carry the same MD3 styling as the internal rendering Select does from its options prop, but Select itself does not expose a slot to compose
them as children — its Select.Content is built entirely from options. These primitives are meant for consumers who need a
custom dropdown built directly against bits-ui's Select.Root instead of this component, and want matching visuals.
Keyboard navigation
Arrow keys move through options, Enter/Space selects, Escape closes — all handled by bits-ui Select primitives.
Selected indicator
The selected item shows a check icon in addition to a colored background, avoiding a color-only signal.
Disabled options
Individual options can be disabled via SelectOption.disabled and are skipped during keyboard navigation.