Skip to content

Select

Displays a list of options for the user to pick from, triggered by a button.

Code
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from "@falcon/ui-kit";
const items = [
{ label: "Select a crop", value: null },
{ label: "Corn", value: "corn" },
{ label: "Soybeans", value: "soybeans" },
{ label: "Wheat", value: "wheat" },
{ label: "Cotton", value: "cotton" },
{ label: "Sorghum", value: "sorghum" },
];
export function Example() {
return (
<div className="tw:flex tw:h-56 tw:items-center tw:justify-center">
<Select items={items}>
<SelectTrigger className="tw:w-full tw:max-w-48" aria-label="Crop">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Crops</SelectLabel>
{items.map((item) => (
<SelectItem key={item.label} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
</div>
);
}

Use the following composition to build a Select:

Select
├── SelectTrigger
│ └── SelectValue
└── SelectContent
├── SelectGroup
│ ├── SelectLabel
│ ├── SelectItem
│ └── SelectItem
├── SelectSeparator
└── SelectGroup
├── SelectLabel
├── SelectItem
└── SelectItem

Use alignItemWithTrigger on SelectContent to control whether the selected item aligns with the trigger. When true (default), the popup positions so the selected item appears over the trigger. When false, the popup aligns to the trigger edge.

Code
import {
Field,
FieldContent,
FieldDescription,
FieldGroup,
FieldLabel,
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
Switch,
} from "@falcon/ui-kit";
import { useState } from "react";
const items = [
{ label: "Select a crop", value: null },
{ label: "Corn", value: "corn" },
{ label: "Soybeans", value: "soybeans" },
{ label: "Wheat", value: "wheat" },
{ label: "Cotton", value: "cotton" },
{ label: "Sorghum", value: "sorghum" },
];
export function Example() {
const [alignItemWithTrigger, setAlignItemWithTrigger] = useState(true);
return (
<FieldGroup className="tw:w-full tw:max-w-xs">
<Field orientation="horizontal">
<FieldContent>
<FieldLabel htmlFor="align-item">Align item</FieldLabel>
<FieldDescription>
Toggle to align the option with the trigger.
</FieldDescription>
</FieldContent>
<Switch
id="align-item"
checked={alignItemWithTrigger}
onCheckedChange={setAlignItemWithTrigger}
/>
</Field>
<Field>
<Select items={items} defaultValue="soybeans">
<SelectTrigger aria-label="Crop">
<SelectValue />
</SelectTrigger>
<SelectContent alignItemWithTrigger={alignItemWithTrigger}>
<SelectGroup>
{items.map((item) => (
<SelectItem key={item.label} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
</Field>
</FieldGroup>
);
}

Use SelectGroup, SelectLabel, and SelectSeparator to organize items.

Code
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectSeparator,
SelectTrigger,
SelectValue,
} from "@falcon/ui-kit";
export function Example() {
const grains = [
{ label: "Corn", value: "corn" },
{ label: "Soybeans", value: "soybeans" },
{ label: "Wheat", value: "wheat" },
];
const cropInputs = [
{ label: "Seed", value: "seed" },
{ label: "Fertilizer", value: "fertilizer" },
{ label: "Crop protection", value: "crop-protection" },
];
const allItems = [
{ label: "Select a category", value: null },
...grains,
...cropInputs,
];
return (
<Select items={allItems}>
<SelectTrigger
className="tw:w-full tw:max-w-48"
aria-label="Product category"
>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Grains</SelectLabel>
{grains.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectGroup>
<SelectSeparator />
<SelectGroup>
<SelectLabel>Crop inputs</SelectLabel>
{cropInputs.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
);
}

A select with many items that scrolls.

Code
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from "@falcon/ui-kit";
const northAmerica = [
{ label: "Eastern Standard Time", value: "est" },
{ label: "Central Standard Time", value: "cst" },
{ label: "Mountain Standard Time", value: "mst" },
{ label: "Pacific Standard Time", value: "pst" },
{ label: "Alaska Standard Time", value: "akst" },
{ label: "Hawaii Standard Time", value: "hst" },
];
const europeAfrica = [
{ label: "Greenwich Mean Time", value: "gmt" },
{ label: "Central European Time", value: "cet" },
{ label: "Eastern European Time", value: "eet" },
{ label: "Western European Summer Time", value: "west" },
{ label: "Central Africa Time", value: "cat" },
{ label: "East Africa Time", value: "eat" },
];
const asia = [
{ label: "Moscow Time", value: "msk" },
{ label: "India Standard Time", value: "ist" },
{ label: "China Standard Time", value: "cst_china" },
{ label: "Japan Standard Time", value: "jst" },
{ label: "Korea Standard Time", value: "kst" },
{ label: "Indonesia Central Standard Time", value: "ist_indonesia" },
];
const australiaPacific = [
{ label: "Australian Western Standard Time", value: "awst" },
{ label: "Australian Central Standard Time", value: "acst" },
{ label: "Australian Eastern Standard Time", value: "aest" },
{ label: "New Zealand Standard Time", value: "nzst" },
{ label: "Fiji Time", value: "fjt" },
];
const southAmerica = [
{ label: "Argentina Time", value: "art" },
{ label: "Bolivia Time", value: "bot" },
{ label: "Brasilia Time", value: "brt" },
{ label: "Chile Standard Time", value: "clt" },
];
const items = [
{ label: "Select a timezone", value: null },
...northAmerica,
...europeAfrica,
...asia,
...australiaPacific,
...southAmerica,
];
export function Example() {
return (
<Select items={items}>
<SelectTrigger className="tw:w-full tw:max-w-64" aria-label="Time zone">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>North America</SelectLabel>
{northAmerica.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectGroup>
<SelectGroup>
<SelectLabel>Europe & Africa</SelectLabel>
{europeAfrica.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectGroup>
<SelectGroup>
<SelectLabel>Asia</SelectLabel>
{asia.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectGroup>
<SelectGroup>
<SelectLabel>Australia & Pacific</SelectLabel>
{australiaPacific.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectGroup>
<SelectGroup>
<SelectLabel>South America</SelectLabel>
{southAmerica.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
);
}
Code
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from "@falcon/ui-kit";
export function Example() {
const items = [
{ label: "Select an order status", value: null },
{ label: "Draft", value: "draft" },
{ label: "Confirmed", value: "confirmed" },
{ label: "In progress", value: "in-progress" },
{ label: "Canceled", value: "canceled", disabled: true },
{ label: "Fulfilled", value: "fulfilled" },
];
return (
<Select items={items} disabled>
<SelectTrigger
className="tw:w-full tw:max-w-48"
aria-label="Order status"
>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{items.map((item) => (
<SelectItem
key={item.label}
value={item.value}
disabled={item.disabled}
>
{item.label}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
);
}

Set data-invalid on Field and aria-invalid on SelectTrigger to show an error state.

<Field data-invalid>
<FieldLabel htmlFor="order-status">Order status</FieldLabel>
<SelectTrigger id="order-status" aria-invalid>
<SelectValue />
</SelectTrigger>
</Field>
Code
import {
Field,
FieldError,
FieldLabel,
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from "@falcon/ui-kit";
const items = [
{ label: "Select an order status", value: null },
{ label: "Draft", value: "draft" },
{ label: "Confirmed", value: "confirmed" },
{ label: "Fulfilled", value: "fulfilled" },
];
export function Example() {
return (
<Field data-invalid className="tw:w-full tw:max-w-48">
<FieldLabel htmlFor="order-status">Order status</FieldLabel>
<Select items={items}>
<SelectTrigger
id="order-status"
aria-describedby="order-status-error"
aria-invalid
>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{items.map((item) => (
<SelectItem key={item.label} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
<FieldError id="order-status-error">
Please select an order status.
</FieldError>
</Field>
);
}

Groups all parts of the select. Does not render its own HTML element.

autoComplete?: string

Provides a hint to the browser for autofill.

children?: React.ReactNode

The select trigger and popup content.

defaultOpen?: boolean

Whether the select popup is initially open. Use open for controlled state. Defaults to false.

defaultValue?: SelectValueType<Value, Multiple> | null

The uncontrolled value of the select. Use value for controlled state.

disabled?: boolean

Whether the component ignores user interaction. Defaults to false.

form?: string

Identifies the form that owns the hidden input.

highlightItemOnHover?: boolean

Whether moving the pointer over items highlights them. Defaults to true.

id?: string

The ID of the select.

inputRef?: React.Ref<HTMLInputElement>

A ref to access the hidden input element.

isItemEqualToValue?: ((itemValue: Value, value: Value) => boolean)

Custom comparison logic for item values and the selected value. Defaults to Object.is.

items?: readonly Group<any>[] | Record<string, React.ReactNode> | readonly { label: React.ReactNode; value: any; }[]

Data for items rendered in the select popup.

itemToStringLabel?: ((itemValue: Value) => string)

Converts object item values to labels displayed in the trigger.

itemToStringValue?: ((itemValue: Value) => string)

Converts object item values to strings for form submission.

modal?: boolean

Whether the open select limits outside interaction. Defaults to true.

multiple?: Multiple

Whether multiple items can be selected. Defaults to false.

name?: string

Identifies the field when a form is submitted.

onOpenChange?: ((open: boolean, eventDetails: SelectRootChangeEventDetails) => void)

Event handler called when the select popup is opened or closed.

onOpenChangeComplete?: ((open: boolean) => void)

Event handler called after select popup animations complete.

onValueChange?: ((value: SelectValueType<Value, Multiple> | (Multiple extends true ? never : null), eventDetails: SelectRootChangeEventDetails) => void)

Event handler called when the selected value changes.

open?: boolean

Whether the select popup is currently open.

readOnly?: boolean

Whether users can choose a different option. Defaults to false.

required?: boolean

Whether a value is required for form submission. Defaults to false.

value?: SelectValueType<Value, NoInfer<Multiple>> | null

The controlled value of the select.

Includes the portal, positioned popup, list, and scroll controls. Renders a <div> element by default and accepts standard <div> props. Pair it with SelectItem components.

align?: Align = "center"

Popup alignment relative to its trigger. Defaults to "center".

alignItemWithTrigger?: boolean = true

Whether the popup aligns item text with the trigger. Defaults to true.

alignOffset?: number | OffsetFunction = 0

Alignment-axis offset in pixels. Defaults to 0.

children?: React.ReactNode

The select items and groups.

className?: string

CSS class applied to the select popup.

finalFocus?: boolean | React.RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | HTMLElement | null | void)

Determines the element to focus when the select popup is closed.

render?: React.ReactElement<any, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SelectPopupState>

Allows replacing the popup element or composing it with another component.

side?: Side = "bottom"

Side of the trigger where the popup is placed. Defaults to "bottom".

sideOffset?: number | OffsetFunction = 4

Distance between the trigger and popup in pixels. Defaults to 4.

style?: React.CSSProperties | ((state: SelectPopupState) => React.CSSProperties | undefined)

Style applied to the popup, or a function based on its state.

Groups related select items with a corresponding label. Renders a <div> element by default and accepts standard <div> props.

className?: string

CSS class applied to the group.

render?: React.ReactElement<any, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SelectGroupState>

Allows replacing the group element or composing it with another component.

style?: React.CSSProperties | ((state: SelectGroupState) => React.CSSProperties | undefined)

Style applied to the group, or a function based on its state.

Selects a value in the parent select. Renders a <div> element by default and accepts standard <div> props.

children?: React.ReactNode

The item’s visible label and optional icon.

className?: string

CSS class applied to the item.

disabled?: boolean

Whether the component ignores user interaction. Defaults to false.

label?: string

Text used to match the item during keyboard text navigation.

nativeButton?: boolean

Whether render produces a native <button> element. Defaults to false.

render?: React.ReactElement<any, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SelectItemState>

Allows replacing the item element or composing it with another component.

style?: React.CSSProperties | ((state: SelectItemState) => React.CSSProperties | undefined)

Style applied to the item, or a function based on its state.

value?: any

A unique value that identifies this select item. Defaults to null.

Labels its parent select group. Renders a <div> element by default and accepts standard <div> props.

className?: string

CSS class applied to the group label.

render?: React.ReactElement<any, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SelectGroupLabelState>

Allows replacing the label element or composing it with another component.

style?: React.CSSProperties | ((state: SelectGroupLabelState) => React.CSSProperties | undefined)

Style applied to the label, or a function based on its state.

Scrolls the select popup down while hovered and does not render for touch input. Renders a <div> element by default and accepts standard <div> props.

className?: string

CSS class applied to the scroll control.

keepMounted?: boolean

Whether the element stays mounted when not scrollable. Defaults to false.

render?: React.ReactElement<any, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SelectScrollDownArrowState>

Allows replacing the scroll control element or composing it with another component.

style?: React.CSSProperties | ((state: SelectScrollDownArrowState) => React.CSSProperties | undefined)

Style applied to the scroll control, or a function based on its state.

Scrolls the select popup up while hovered and does not render for touch input. Renders a <div> element by default and accepts standard <div> props.

className?: string

CSS class applied to the scroll control.

keepMounted?: boolean

Whether the element stays mounted when not scrollable. Defaults to false.

render?: React.ReactElement<any, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SelectScrollUpArrowState>

Allows replacing the scroll control element or composing it with another component.

style?: React.CSSProperties | ((state: SelectScrollUpArrowState) => React.CSSProperties | undefined)

Style applied to the scroll control, or a function based on its state.

Separates select items. Renders a <div> element by default and accepts standard <div> props.

className?: string

CSS class applied to the separator.

orientation?: Orientation

The separator’s orientation. Defaults to "horizontal".

render?: React.ReactElement<any, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SeparatorState>

Allows replacing the separator element or composing it with another component.

style?: React.CSSProperties | ((state: SeparatorState) => React.CSSProperties | undefined)

Style applied to the separator, or a function based on its state.

Opens the select popup. Renders a <button> element by default, accepts standard <button> props, and includes a disclosure icon.

children?: React.ReactNode

The trigger’s selected value.

className?: string

CSS class applied to the trigger.

disabled?: boolean

Whether the component ignores user interaction. Defaults to false.

nativeButton?: boolean

Whether render produces a native <button> element. Defaults to true.

render?: React.ReactElement<any, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SelectTriggerState>

Allows replacing the trigger element or composing it with another component.

size?: "sm" | "default" = "default"

Controls the trigger’s dimensions. Defaults to "default".

style?: React.CSSProperties | ((state: SelectTriggerState) => React.CSSProperties | undefined)

Style applied to the trigger, or a function based on its state.

Displays the selected value or placeholder. Renders a <span> element by default and accepts standard <span> props.

children?: React.ReactNode | ((value: any) => React.ReactNode)

Content or a function that formats the selected value.

className?: string

CSS class applied to the selected value.

placeholder?: React.ReactNode

The value displayed when no item is selected.

render?: React.ReactElement<any, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SelectValueState>

Allows replacing the value element or composing it with another component.

style?: React.CSSProperties | ((state: SelectValueState) => React.CSSProperties | undefined)

Style applied to the value, or a function based on its state.