SelectPreview
A select displays a collapsible list of options and allows a user to select one of them
Import
import { Select } from '@heroui/react';Usage
import {Label, ListBox, Select} from "@heroui/react";
export function Default() {
return (
<Select className="w-[256px]" placeholder="Select one">
<Label>State</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox>
<ListBox.Item id="florida" textValue="Florida">
Florida
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="delaware" textValue="Delaware">
Delaware
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="california" textValue="California">
California
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="texas" textValue="Texas">
Texas
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="new-york" textValue="New York">
New York
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="washington" textValue="Washington">
Washington
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Select.Content>
</Select>
);
}Anatomy
Import the Select component and access all parts using dot notation.
import { Select, Label, Description, Header, ListBox, Separator } from '@heroui/react';
export default () => (
<Select>
<Label />
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Description />
<Select.Content>
<ListBox>
<ListBox.Item>
<Label />
<Description />
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Section>
<Header />
<ListBox.Item>
<Label />
</ListBox.Item>
</ListBox.Section>
</ListBox>
</Select.Content>
</Select>
)With Description
import {Description, Label, ListBox, Select} from "@heroui/react";
export function WithDescription() {
return (
<Select className="w-[256px]" placeholder="Select one">
<Label>State</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox>
<ListBox.Item id="florida" textValue="Florida">
Florida
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="delaware" textValue="Delaware">
Delaware
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="california" textValue="California">
California
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="texas" textValue="Texas">
Texas
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="new-york" textValue="New York">
New York
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="washington" textValue="Washington">
Washington
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Select.Content>
<Description>Select your state of residence</Description>
</Select>
);
}Multiple Select
import {Label, ListBox, Select} from "@heroui/react";
export function MultipleSelect() {
return (
<Select className="w-[256px]" placeholder="Select countries" selectionMode="multiple">
<Label>Countries to Visit</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox selectionMode="multiple">
<ListBox.Item id="argentina" textValue="Argentina">
Argentina
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="venezuela" textValue="Venezuela">
Venezuela
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="japan" textValue="Japan">
Japan
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="france" textValue="France">
France
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="italy" textValue="Italy">
Italy
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="spain" textValue="Spain">
Spain
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="thailand" textValue="Thailand">
Thailand
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="new-zealand" textValue="New Zealand">
New Zealand
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="iceland" textValue="Iceland">
Iceland
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Select.Content>
</Select>
);
}With Sections
import {Header, Label, ListBox, Select, Separator} from "@heroui/react";
export function WithSections() {
return (
<Select className="w-[256px]" placeholder="Select a country">
<Label>Country</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox>
<ListBox.Section>
<Header>North America</Header>
<ListBox.Item id="usa" textValue="United States">
United States
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="canada" textValue="Canada">
Canada
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="mexico" textValue="Mexico">
Mexico
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox.Section>
<Separator />
<ListBox.Section>
<Header>Europe</Header>
<ListBox.Item id="uk" textValue="United Kingdom">
United Kingdom
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="france" textValue="France">
France
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="germany" textValue="Germany">
Germany
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="spain" textValue="Spain">
Spain
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="italy" textValue="Italy">
Italy
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox.Section>
<Separator />
<ListBox.Section>
<Header>Asia</Header>
<ListBox.Item id="japan" textValue="Japan">
Japan
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="china" textValue="China">
China
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="india" textValue="India">
India
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="south-korea" textValue="South Korea">
South Korea
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox.Section>
</ListBox>
</Select.Content>
</Select>
);
}With Disabled Options
import {Label, ListBox, Select} from "@heroui/react";
export function WithDisabledOptions() {
return (
<Select className="w-[256px]" disabledKeys={["cat", "kangaroo"]} placeholder="Select an animal">
<Label>Animal</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox>
<ListBox.Item id="dog" textValue="Dog">
Dog
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="cat" textValue="Cat">
Cat
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="bird" textValue="Bird">
Bird
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="kangaroo" textValue="Kangaroo">
Kangaroo
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="elephant" textValue="Elephant">
Elephant
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="tiger" textValue="Tiger">
Tiger
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Select.Content>
</Select>
);
}Custom Indicator
import {Label, ListBox, Select} from "@heroui/react";
import {Icon} from "@iconify/react";
export function CustomIndicator() {
return (
<Select className="w-[256px]" placeholder="Select one">
<Label>State</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator className="size-3">
<Icon icon="gravity-ui:chevrons-expand-vertical" />
</Select.Indicator>
</Select.Trigger>
<Select.Content>
<ListBox>
<ListBox.Item id="florida" textValue="Florida">
Florida
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="delaware" textValue="Delaware">
Delaware
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="california" textValue="California">
California
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="texas" textValue="Texas">
Texas
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="new-york" textValue="New York">
New York
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="washington" textValue="Washington">
Washington
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Select.Content>
</Select>
);
}Required
"use client";
import {Button, FieldError, Form, Label, ListBox, Select} from "@heroui/react";
export function Required() {
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const data: Record<string, string> = {};
// Convert FormData to plain object
formData.forEach((value, key) => {
data[key] = value.toString();
});
alert("Form submitted successfully!");
};
return (
<Form className="flex w-[256px] flex-col gap-4" onSubmit={onSubmit}>
<Select isRequired className="w-full" name="state" placeholder="Select one">
<Label>State</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox>
<ListBox.Item id="florida" textValue="Florida">
Florida
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="delaware" textValue="Delaware">
Delaware
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="california" textValue="California">
California
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="texas" textValue="Texas">
Texas
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="new-york" textValue="New York">
New York
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="washington" textValue="Washington">
Washington
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Select.Content>
<FieldError />
</Select>
<Select isRequired className="w-full" name="country" placeholder="Select a country">
<Label>Country</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox>
<ListBox.Item id="usa" textValue="United States">
United States
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="canada" textValue="Canada">
Canada
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="mexico" textValue="Mexico">
Mexico
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="uk" textValue="United Kingdom">
United Kingdom
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="france" textValue="France">
France
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="germany" textValue="Germany">
Germany
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Select.Content>
<FieldError />
</Select>
<Button type="submit">Submit</Button>
</Form>
);
}On Surface
"use client";
import {Button, FieldError, Form, Label, ListBox, Select, Surface} from "@heroui/react";
export function OnSurface() {
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const data: Record<string, string> = {};
// Convert FormData to plain object
formData.forEach((value, key) => {
data[key] = value.toString();
});
alert("Form submitted successfully!");
};
return (
<Surface className="w-[320px] rounded-3xl p-6">
<Form className="flex w-full flex-col gap-4" onSubmit={onSubmit}>
<Select isRequired className="w-full" name="state" placeholder="Select one">
<Label>State</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox>
<ListBox.Item id="florida" textValue="Florida">
Florida
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="delaware" textValue="Delaware">
Delaware
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="california" textValue="California">
California
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="texas" textValue="Texas">
Texas
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="new-york" textValue="New York">
New York
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="washington" textValue="Washington">
Washington
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Select.Content>
<FieldError />
</Select>
<Select isRequired className="w-full" name="country" placeholder="Select a country">
<Label>Country</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox>
<ListBox.Item id="usa" textValue="United States">
United States
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="canada" textValue="Canada">
Canada
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="mexico" textValue="Mexico">
Mexico
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="uk" textValue="United Kingdom">
United Kingdom
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="france" textValue="France">
France
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="germany" textValue="Germany">
Germany
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Select.Content>
<FieldError />
</Select>
<Button type="submit">Submit</Button>
</Form>
</Surface>
);
}Custom Value
"use client";
import {
Avatar,
AvatarFallback,
AvatarImage,
Description,
Label,
ListBox,
Select,
} from "@heroui/react";
export function CustomValue() {
const users = [
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg",
email: "bob@heroui.com",
fallback: "B",
id: "1",
name: "Bob",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg",
email: "fred@heroui.com",
fallback: "F",
id: "2",
name: "Fred",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg",
email: "martha@heroui.com",
fallback: "M",
id: "3",
name: "Martha",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/red.jpg",
email: "john@heroui.com",
fallback: "J",
id: "4",
name: "John",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg",
email: "jane@heroui.com",
fallback: "J",
id: "5",
name: "Jane",
},
];
return (
<Select className="w-[256px]" placeholder="Select a user">
<Label>User</Label>
<Select.Trigger>
<Select.Value>
{({defaultChildren, isPlaceholder, state}) => {
if (isPlaceholder || state.selectedItems.length === 0) {
return defaultChildren;
}
const selectedItems = state.selectedItems;
if (selectedItems.length > 1) {
return `${selectedItems.length} users selected`;
}
const selectedItem = users.find((user) => user.id === selectedItems[0].key);
if (!selectedItem) {
return defaultChildren;
}
return (
<div className="flex items-center gap-2">
<Avatar className="size-4" size="sm">
<AvatarImage src={selectedItem.avatarUrl} />
<AvatarFallback>{selectedItem.fallback}</AvatarFallback>
</Avatar>
<span>{selectedItem.name}</span>
</div>
);
}}
</Select.Value>
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox>
{users.map((user) => (
<ListBox.Item key={user.id} id={user.id} textValue={user.name}>
<Avatar size="sm">
<AvatarImage src={user.avatarUrl} />
<AvatarFallback>{user.fallback}</AvatarFallback>
</Avatar>
<div className="flex flex-col">
<Label>{user.name}</Label>
<Description>{user.email}</Description>
</div>
<ListBox.ItemIndicator />
</ListBox.Item>
))}
</ListBox>
</Select.Content>
</Select>
);
}Controlled
Selected: California
"use client";
import type {Key} from "react-aria-components";
import {Label, ListBox, Select} from "@heroui/react";
import {useState} from "react";
export function Controlled() {
const states = [
{
id: "california",
name: "California",
},
{
id: "texas",
name: "Texas",
},
{
id: "florida",
name: "Florida",
},
{
id: "new-york",
name: "New York",
},
{
id: "illinois",
name: "Illinois",
},
{
id: "pennsylvania",
name: "Pennsylvania",
},
];
const [state, setState] = useState<Key | null>("california");
const selectedState = states.find((s) => s.id === state);
return (
<div className="space-y-2">
<Select
className="w-[256px]"
placeholder="Select a state"
value={state}
onChange={(value) => setState(value)}
>
<Label>State (controlled)</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox>
{states.map((state) => (
<ListBox.Item key={state.id} id={state.id} textValue={state.name}>
{state.name}
<ListBox.ItemIndicator />
</ListBox.Item>
))}
</ListBox>
</Select.Content>
</Select>
<p className="text-muted text-sm">Selected: {selectedState?.name || "None"}</p>
</div>
);
}Controlled Multiple
Selected: california, texas
"use client";
import type {Selection} from "@react-types/shared";
import {Label, ListBox, Select} from "@heroui/react";
import {useState} from "react";
export function ControlledMultiple() {
const [selected, setSelected] = useState<Selection>(new Set(["california", "texas"]));
const selectedItems = Array.from(selected);
return (
<div className="space-y-4">
<Select className="w-[256px]" placeholder="Select states" selectionMode="multiple">
<Label>States (controlled multiple)</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox
selectedKeys={selected}
selectionMode="multiple"
onSelectionChange={(keys) => setSelected(keys as Selection)}
>
<ListBox.Item id="california" textValue="California">
California
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="texas" textValue="Texas">
Texas
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="florida" textValue="Florida">
Florida
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="new-york" textValue="New York">
New York
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="illinois" textValue="Illinois">
Illinois
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="pennsylvania" textValue="Pennsylvania">
Pennsylvania
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Select.Content>
</Select>
<p className="text-sm text-neutral-500">
Selected: {selectedItems.length > 0 ? selectedItems.join(", ") : "None"}
</p>
</div>
);
}Controlled Open State
Select is closed
"use client";
import {Button, Label, ListBox, Select} from "@heroui/react";
import {useState} from "react";
export function ControlledOpenState() {
const [isOpen, setIsOpen] = useState(false);
return (
<div className="space-y-4">
<Select
className="w-[256px]"
isOpen={isOpen}
placeholder="Select one"
onOpenChange={setIsOpen}
>
<Label>State</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox>
<ListBox.Item id="florida" textValue="Florida">
Florida
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="delaware" textValue="Delaware">
Delaware
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="california" textValue="California">
California
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="texas" textValue="Texas">
Texas
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="new-york" textValue="New York">
New York
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="washington" textValue="Washington">
Washington
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Select.Content>
</Select>
<Button onPress={() => setIsOpen(!isOpen)}>{isOpen ? "Close" : "Open"} Select</Button>
<p className="text-sm text-neutral-500">Select is {isOpen ? "open" : "closed"}</p>
</div>
);
}Asynchronous Loading
"use client";
import {Label, ListBox, Select, Spinner} from "@heroui/react";
import {useAsyncList} from "@react-stately/data";
import {Collection, ListBoxLoadMoreItem} from "react-aria-components";
interface Pokemon {
name: string;
}
export function AsynchronousLoading() {
const list = useAsyncList<Pokemon>({
async load({cursor, signal}) {
const res = await fetch(cursor || `https://pokeapi.co/api/v2/pokemon`, {signal});
const json = await res.json();
return {
cursor: json.next,
items: json.results,
};
},
});
return (
<Select className="w-[256px]" placeholder="Select a Pokemon">
<Label>Pick a Pokemon</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox>
<Collection items={list.items}>
{(item: Pokemon) => (
<ListBox.Item id={item.name} textValue={item.name}>
{item.name}
<ListBox.ItemIndicator />
</ListBox.Item>
)}
</Collection>
<ListBoxLoadMoreItem
isLoading={list.loadingState === "loadingMore"}
onLoadMore={list.loadMore}
>
<div className="flex items-center justify-center gap-2 py-2">
<Spinner size="sm" />
<span className="text-sm text-neutral-500">Loading more...</span>
</div>
</ListBoxLoadMoreItem>
</ListBox>
</Select.Content>
</Select>
);
}Disabled
import {Label, ListBox, Select} from "@heroui/react";
export function Disabled() {
return (
<div className="flex flex-col gap-4">
<Select isDisabled className="w-[256px]" defaultValue="california" placeholder="Select one">
<Label>State</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox>
<ListBox.Item id="florida" textValue="Florida">
Florida
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="delaware" textValue="Delaware">
Delaware
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="california" textValue="California">
California
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="texas" textValue="Texas">
Texas
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="new-york" textValue="New York">
New York
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="washington" textValue="Washington">
Washington
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Select.Content>
</Select>
<Select
isDisabled
className="w-[256px]"
defaultValue={["argentina", "japan", "france"]}
placeholder="Select countries"
selectionMode="multiple"
>
<Label>Countries to Visit</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox>
<ListBox.Item id="argentina" textValue="Argentina">
Argentina
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="venezuela" textValue="Venezuela">
Venezuela
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="japan" textValue="Japan">
Japan
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="france" textValue="France">
France
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="italy" textValue="Italy">
Italy
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="spain" textValue="Spain">
Spain
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Select.Content>
</Select>
</div>
);
}Styling
Passing Tailwind CSS classes
import { Select } from '@heroui/react';
function CustomSelect() {
return (
<Select className="w-full">
<Label>State</Label>
<Select.Trigger className="border rounded-lg p-2 bg-surface">
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox>
<ListBox.Item id="1" textValue="Item 1" className="hover:bg-surface-secondary">
Item 1
</ListBox.Item>
</ListBox>
</Select.Content>
</Select>
);
}Customizing the component classes
To customize the Select component classes, you can use the @layer components directive.
Learn more.
@layer components {
.select {
@apply flex flex-col gap-1;
}
.select__trigger {
@apply rounded-lg border border-border bg-surface p-2;
}
.select__value {
@apply text-current;
}
.select__indicator {
@apply text-muted;
}
.select__content {
@apply rounded-lg border border-border bg-surface p-2;
}
}HeroUI follows the BEM methodology to ensure component variants and states are reusable and easy to customize.
CSS Classes
The Select component uses these CSS classes (View source styles):
Base Classes
.select- Base select container.select__trigger- The button that triggers the select.select__value- The displayed value or placeholder.select__indicator- The dropdown indicator icon.select__content- The popover content container
Variant Classes
.select__trigger--on-surface- On surface variant styling
State Classes
.select[data-invalid="true"]- Invalid state.select__trigger[data-focus-visible="true"]- Focused trigger state.select__trigger[data-disabled="true"]- Disabled trigger state.select__value[data-placeholder="true"]- Placeholder state.select__indicator[data-open="true"]- Open indicator state
Interactive States
The component supports both CSS pseudo-classes and data attributes for flexibility:
- Hover:
:hoveror[data-hovered="true"]on trigger - Focus:
:focus-visibleor[data-focus-visible="true"]on trigger - Disabled:
:disabledor[data-disabled="true"]on select - Open:
[data-open="true"]on indicator
API Reference
Select Props
| Prop | Type | Default | Description |
|---|---|---|---|
placeholder | string | 'Select an item' | Temporary text that occupies the select when it is empty |
selectionMode | "single" | "multiple" | "single" | Whether single or multiple selection is enabled |
isOpen | boolean | - | Sets the open state of the menu (controlled) |
defaultOpen | boolean | - | Sets the default open state of the menu (uncontrolled) |
onOpenChange | (isOpen: boolean) => void | - | Handler called when the open state changes |
disabledKeys | Iterable<Key> | - | Keys of disabled items |
isDisabled | boolean | - | Whether the select is disabled |
value | Key | Key[] | null | - | Current value (controlled) |
defaultValue | Key | Key[] | null | - | Default value (uncontrolled) |
onChange | (value: Key | Key[] | null) => void | - | Handler called when the value changes |
isRequired | boolean | - | Whether user input is required |
isInvalid | boolean | - | Whether the select value is invalid |
name | string | - | The name of the input, used when submitting an HTML form |
autoComplete | string | - | Describes the type of autocomplete functionality |
isOnSurface | boolean | - | Whether the select is displayed on a surface component |
className | string | - | Additional CSS classes |
children | ReactNode | RenderFunction | - | Select content or render function |
Select.Trigger Props
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional CSS classes |
children | ReactNode | RenderFunction | - | Trigger content or render function |
Select.Value Props
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional CSS classes |
children | ReactNode | RenderFunction | - | Value content or render function |
Select.Indicator Props
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional CSS classes |
children | ReactNode | - | Custom indicator content |
Select.Content Props
| Prop | Type | Default | Description |
|---|---|---|---|
placement | "bottom" | "bottom left" | "bottom right" | "bottom start" | "bottom end" | "top" | "top left" | "top right" | "top start" | "top end" | "left" | "left top" | "left bottom" | "start" | "start top" | "start bottom" | "right" | "right top" | "right bottom" | "end" | "end top" | "end bottom" | "bottom" | Placement of the popover relative to the trigger |
className | string | - | Additional CSS classes |
children | ReactNode | - | Content children |
RenderProps
When using render functions with Select.Value, these values are provided:
| Prop | Type | Description |
|---|---|---|
defaultChildren | ReactNode | The default rendered value |
isPlaceholder | boolean | Whether the value is a placeholder |
state | SelectState | The state of the select |
selectedItems | Node[] | The currently selected items |
Examples
Basic Usage
import { Select, Label, ListBox } from '@heroui/react';
<Select className="w-[256px]" placeholder="Select one">
<Label>State</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox>
<ListBox.Item id="florida" textValue="Florida">
Florida
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="california" textValue="California">
California
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Select.Content>
</Select>With Sections
import { Select, Label, ListBox, Header, Separator } from '@heroui/react';
<Select className="w-[256px]" placeholder="Select a country">
<Label>Country</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox>
<ListBox.Section>
<Header>North America</Header>
<ListBox.Item id="usa" textValue="United States">
United States
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox.Section>
<Separator />
<ListBox.Section>
<Header>Europe</Header>
<ListBox.Item id="uk" textValue="United Kingdom">
United Kingdom
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox.Section>
</ListBox>
</Select.Content>
</Select>Controlled Selection
import type { Key } from '@heroui/react';
import { Select, Label, ListBox } from '@heroui/react';
import { useState } from 'react';
function ControlledSelect() {
const [value, setValue] = useState<Key | null>('california');
return (
<Select
className="w-[256px]"
placeholder="Select a state"
value={value}
onChange={setValue}
>
<Label>State</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox>
<ListBox.Item id="california" textValue="California">
California
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="texas" textValue="Texas">
Texas
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Select.Content>
</Select>
);
}Custom Value Display
import { Select, Label, ListBox, Avatar, AvatarImage, AvatarFallback } from '@heroui/react';
<Select className="w-[256px]" placeholder="Select a user">
<Label>User</Label>
<Select.Trigger>
<Select.Value>
{({defaultChildren, isPlaceholder, state}) => {
if (isPlaceholder || state.selectedItems.length === 0) {
return defaultChildren;
}
const selectedItem = users.find((user) => user.id === state.selectedItems[0].key);
if (!selectedItem) {
return defaultChildren;
}
return (
<div className="flex items-center gap-2">
<Avatar className="size-4" size="sm">
<AvatarImage src={selectedItem.avatarUrl} />
<AvatarFallback>{selectedItem.fallback}</AvatarFallback>
</Avatar>
<span>{selectedItem.name}</span>
</div>
);
}}
</Select.Value>
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox>
{/* ListBox items */}
</ListBox>
</Select.Content>
</Select>Accessibility
The Select component implements the ARIA listbox pattern and provides:
- Full keyboard navigation support
- Screen reader announcements for selection changes
- Proper focus management
- Support for disabled states
- Typeahead search functionality
- HTML form integration
For more information, see the React Aria Select documentation.