Ryo Matsukawa c659e2f215
chore(docs): add note itemHeight for virtualization (#4822)
* chore(docs): add note itemHeight for virtualization

* fix: format
2025-02-11 10:02:30 -03:00

720 lines
24 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: "Select"
description: "A select displays a collapsible list of options and allows a user to select one or more of them."
---
import {selectContent} from "@/content/components/select";
# Select
A select displays a collapsible list of options and allows a user to select one or more of them.
<ComponentLinks component="select" reactAriaHook="useSelect" />
---
<CarbonAd/>
## Installation
<PackageManagers
showGlobalInstallWarning
commands={{
cli: "npx heroui-cli@latest add select",
npm: "npm install @heroui/select",
yarn: "yarn add @heroui/select",
pnpm: "pnpm add @heroui/select",
bun: "bun add @heroui/select"
}}
/>
## Import
HeroUI exports 3 select-related components:
- **Select**: The main component, which is a wrapper for the other components.
- **SelectSection**: The component that contains a group of select items.
- **SelectItem**: The component that represents a select item.
<ImportTabs
commands={{
main: 'import {Select, SelectSection, SelectItem} from "@heroui/react";',
individual: 'import {Select, SelectSection, SelectItem} from "@heroui/select";',
}}
/>
## Usage
<CodeDemo title="Usage" files={selectContent.usage} />
### Dynamic items
Select follows the [Collection Components API](https://react-spectrum.adobe.com/react-stately/collections.html), accepting both static and dynamic collections.
- **Static**: The usage example above shows the static implementation, which can be used when the full list of options is known ahead of time.
- **Dynamic**: The example below can be used when the options come from an external data source such as an API call, or update over time.
<CodeDemo title="Dynamic items" files={selectContent.dynamic} />
### Multiple Selection
You can use the `selectionMode="multiple"` property to allow multiple selection.
<CodeDemo title="Multiple Selection" files={selectContent.multiple} />
### Disabled
<CodeDemo title="Disabled" files={selectContent.disabled} />
### Disabled Items
You can disable specific items by using the `disabledKeys` property.
<CodeDemo title="Disabled Items" files={selectContent.disabledItems} />
### Required
If you pass the `isRequired` property to the select, it will have a `danger` asterisk at
the end of the label and the select will be required.
<CodeDemo title="Required" files={selectContent.required} />
### Sizes
<CodeDemo title="Sizes" files={selectContent.sizes} />
### Colors
<CodeDemo title="Colors" files={selectContent.colors} />
### Variants
<CodeDemo title="Variants" files={selectContent.variants} />
### Radius
<CodeDemo title="Radius" files={selectContent.radius} />
### Label Placements
You can change the position of the label by setting the `labelPlacement` property to `inside`, `outside` or `outside-left`.
<CodeDemo title="Label Placements" files={selectContent.labelPlacements} />
> **Note**: If the `label` is not passed, the `labelPlacement` property will be `outside` by default.
### Start Content
You can use the `startContent` and `endContent` properties to add content to the start and end of
the select.
<CodeDemo title="Start Content" files={selectContent.startContent} />
### Item Start & End Content
Since the `Select` component uses the [Listbox](/docs/components/listbox) component under the hood, you can
use the `startContent` and `endContent` properties of the `SelectItem` component to add content to the start
and end of the select item.
<CodeDemo title="Item Start Content" files={selectContent.itemStartContent} />
### Custom Selector Icon
By default the select uses a `chevron-down` icon as the selector icon which rotates when the select is open. You can
customize this icon by passing a custom one to the `selectorIcon` property.
<CodeDemo title="Custom Selector Icon" files={selectContent.customSelectorIcon} />
> **Note**: Use the `disableSelectorIconRotation` property to disable the rotation of the icon.
### Without Scroll Shadow
Select component uses the [ScrollShadow](/docs/components/scroll-shadow) under the hood to show a shadow when the select content is scrollable.
You can disable this shadow by passing using the `scrollShadowProps` property.
<CodeDemo title="Without Scroll Shadow" files={selectContent.withoutScrollShadow} />
> **Note**: You can also use the `showScrollIndicators` property to disable the scroll indicators.
### With Description
You can add a description to the select by passing the `description` property.
<CodeDemo title="With Description" files={selectContent.description} />
### With Error Message
You can combine the `isInvalid` and `errorMessage` properties to show an invalid select.
<CodeDemo title="With Error Message" files={selectContent.errorMessage} />
### Controlled
You can use the `selectedKeys` and `onSelectionChange` / `onChange` properties to control the select value.
Using `onSelectionChange`:
<CodeDemo title="Controlled with onSelectionChange" files={selectContent.singleControlled} />
Using `onChange`:
<CodeDemo title="Controlled with onChange" files={selectContent.singleControlledOnChange} />
### Controlling the open state
You can control the open state of the select by using the `isOpen` and `onOpenChange` / `onClose` properties.
<CodeDemo title="Controlling the open state" files={selectContent.openState} />
### Custom Items
You can customize the select items by modifying the `SelectItem` children.
<CodeDemo title="Custom Items" files={selectContent.customItems} />
### Custom Render Value
By default the select will render the selected item's text value, but you can customize this by passing a `renderValue` function.
<CodeDemo title="Custom Render Value" files={selectContent.customRenderValue} />
The `renderValue` function receives the selected items as a parameter and must return a
`ReactNode`. Check the [Render Value Function](#render-value-function) section for more details.
### Asynchronous Loading
Select supports asynchronous loading, in the example below we are using a custom hook to fetch the [Pokemon API](https://pokeapi.co/api/v2/pokemon) data in combination with the `useInfiniteScroll` hook to load more data when the user reaches the end of the list.
The `isLoading` prop is used to show a loading indicator instead of the selector icon when the data is being fetched.
<PackageManagers
commands={{
npm: "npm install @heroui/use-infinite-scroll",
yarn: "yarn add @heroui/use-infinite-scroll",
pnpm: "pnpm add @heroui/use-infinite-scroll",
}}
/>
```jsx
import {useInfiniteScroll} from "@heroui/use-infinite-scroll";
```
<Spacer y={2} />
<CodeDemo
asIframe
typescriptStrict={true}
title="Asynchronous Loading"
hideWindowActions={true}
resizeEnabled={false}
displayMode="always"
files={selectContent.asyncLoadingItems}
previewHeight="400px"
iframeSrc="/examples/select/async-items-loading"
/>
### Virtualization
Select supports virtualization, which allows efficient rendering of large lists by only rendering items that are visible in the viewport. You can enable virtualization by setting the `isVirtualized` prop to `true`.
<CodeDemo
title="Virtualization"
files={selectContent.virtualization}
/>
> **Note**: The virtualization strategy is based on the [@tanstack/react-virtual](https://tanstack.com/virtual/latest) package, which provides efficient rendering of large lists by only rendering items that are visible in the viewport.
#### Ten Thousand Items
Here's an example of using virtualization with 10,000 items.
<CodeDemo title="Ten Thousand Items" files={selectContent.virtualizationTenThousand} />
#### Max Listbox Height
The `maxListboxHeight` prop is used to set the maximum height of the listbox. This is required when using virtualization. By default, it's set to `256`.
<CodeDemo title="Max Listbox Height" files={selectContent.virtualizationMaxListboxHeight} />
#### Custom Item Height
The `itemHeight` prop is used to set the height of each item in the listbox. This is required when using virtualization. By default, it's set to `32`.
> **Note**: If the height of the list items differs from the default due to `startContent` or other custom content, be sure to pass the correct value to `itemHeight` to prevent layout issues.
<CodeDemo title="Custom Item Height" files={selectContent.virtualizationCustomItemHeight} />
### With Sections
You can use the `SelectSection` component to group select items.
<CodeDemo title="With Sections" files={selectContent.sections} />
### Custom Sections Style
You can customize the sections style by using the `classNames` property of the `SelectSection` component.
<CodeDemo title="Custom Sections Style" files={selectContent.customSectionsStyle} />
### Multiple Select Controlled
You can use the same properties as the single select to control the multiple select, `selectedKeys` and `onSelectionChange` / `onChange`.
Using `onSelectionChange`:
<CodeDemo
title="Multiple Selection Controlled with onSelectionChange"
files={selectContent.multipleControlled}
/>
Using `onChange`:
<CodeDemo
title="Multiple Selection Controlled with onChange"
files={selectContent.multipleControlledOnChange}
/>
### Multiple With Chips
You can render any component as the select value by using the `renderValue` property. In this example we are
using the [Chip](/docs/components/chip) component to render the selected items.
<CodeDemo title="Multiple Selection with Chips" files={selectContent.multipleWithChips} />
> **Note**: Make sure to pass the `isMultiline` property to the `Select` component to allow the chips to wrap.
The `renderValue` function receives the selected items as a parameter and must return a
`ReactNode`. Check the [Render Value Function](#render-value-function) section for more details.
### Customizing the select
You can customize any slot of the select by using the `classNames` property. Select
component also provides the [popoverProps](/docs/components/popover#api) and [listboxProps](/docs/components/listbox#api) properties to customize
the popover and listbox components.
<CodeDemo title="Custom Styles" files={selectContent.customStyles} />
## Slots
- **base**: The main wrapper of the select. This wraps the rest of the slots.
- **label**: The label of the select.
- **mainWrapper**: Wraps the `helperWrapper` and the `trigger` slots.
- **trigger**: The trigger of the select. This wraps the label the inner wrapper and the selector icon.
- **innerWrapper**: The wrapper of the select content. This wraps the start/end content and the select value.
- **selectorIcon**: The selector icon of the select. This is the icon that rotates when the select is open (`data-open`).
- **value**: The select value. This is also the slot that wraps the `renderValue` function result.
- **listboxWrapper**: The wrapper of the listbox. This wraps the listbox component, this slot is used on top of the scroll shadow component.
- **listbox**: The listbox component. This is the component that wraps the select items.
- **popoverContent**: The popover content slot. Use this to modify the popover content styles.
- **helperWrapper**: The wrapper of the helper text. This wraps the helper text and the error message.
- **description**: The description of the select.
- **errorMessage**: The error message of the select.
## Data Attributes
`Select` has the following attributes on the `base` element:
- **data-filled**:
Indicates if the select has a value, is focused, has start/end content or is open.
- **data-has-value**:
Indicates if the select has selected item(s).
- **data-has-label**:
Indicates if the select has a label. Based on `label` prop.
- **data-has-helper**:
Indicates if the select has helper text. Based on `errorMessage` or `description` prop.
- **data-invalid**:
Indicates if the select is invalid. Based on `isInvalid` prop.
`Select` has the following attributes on the `trigger` element:
- **data-open**:
Indicates if the select is open.
- **data-disabled**:
When the select trigger is disabled. Based on select `isDisabled` prop.
- **data-focus**:
When the select trigger is being focused. Based on [useFocusRing](https://react-spectrum.adobe.com/react-aria/useFocusRing.html).
- **data-focus-visible**:
When the select trigger is being focused with the keyboard. Based on [useFocusRing](https://react-spectrum.adobe.com/react-aria/useFocusRing.html).
- **data-pressed**:
When the select trigger is pressed. Based on [usePress](https://react-spectrum.adobe.com/react-aria/usePress.html)
- **data-hover**:
When the select trigger is being hovered. Based on [useHover](https://react-spectrum.adobe.com/react-aria/useHover.html)
`Select` has the following attributes on the `selectorIcon` element:
- **data-open**:
Indicates if the select is open.
`SelectItem` has the following attributes on the `base` element:
- **data-disabled**:
When the select item is disabled. Based on select `disabledKeys` prop.
- **data-selected**:
When the select item is selected. Based on select `selectedKeys` prop.
- **data-hover**:
When the select item is being hovered. Based on [useHover](https://react-spectrum.adobe.com/react-aria/useHover.html)
- **data-pressed**:
When the select item is pressed. Based on [usePress](https://react-spectrum.adobe.com/react-aria/usePress.html)
- **data-focus**:
When the select item is being focused. Based on [useFocusRing](https://react-spectrum.adobe.com/react-aria/useFocusRing.html).
- **data-focus-visible**:
When the select item is being focused with the keyboard. Based on [useFocusRing](https://react-spectrum.adobe.com/react-aria/useFocusRing.html).
<Spacer y={4} />
## Accessibility
- Exposed to assistive technology as a button with a listbox popup using ARIA (combined with [Listbox](/docs/components/listbox)).
- Support for selecting a single option.
- Support for selecting multiple options.
- Support for disabled options.
- Support for sections.
- Labeling support for accessibility.
- Support for description and error message help text linked to the input via ARIA.
- Support for mouse, touch, and keyboard interactions.
- Tab stop focus management.
- Keyboard support for opening the listbox using the arrow keys, including automatically focusing the first or last item accordingly.
- Typeahead to allow selecting options by typing text, even without opening the listbox.
- Browser autofill integration via a hidden native `<select>` element.
- Mobile screen reader listbox dismissal support.
<Spacer y={4} />
## API
### Select Props
<APITable
data={[
{
attribute: "children*",
type: "ReactNode[]",
description: "The children to render. Usually a list of SelectItem and SelectSection elements.",
default: "-"
},
{
attribute: "items",
type: "Iterable<T>",
description: "Item objects in the select. (dynamic)",
default: "-"
},
{
attribute: "selectionMode",
type: "single | multiple",
description: "The type of selection that is allowed in the collection.",
default: "-"
},
{
attribute: "selectedKeys",
type: "all | Iterable<React.Key>",
description: "The currently selected keys in the collection (controlled).",
default: "-"
},
{
attribute: "disabledKeys",
type: "Iterable<React.Key>",
description: "The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with.",
default: "-"
},
{
attribute: "defaultSelectedKeys",
type: "all | Iterable<React.Key>",
description: "The initial selected keys in the collection (uncontrolled).",
default: "-"
},
{
attribute: "variant",
type: "flat | bordered | faded | underlined",
description: "The variant of the select.",
default: "flat"
},
{
attribute: "color",
type: "default | primary | secondary | success | warning | danger",
description: "The color of the select.",
default: "default"
},
{
attribute: "size",
type: "sm | md | lg",
description: "The size of the select.",
default: "md"
},
{
attribute: "radius",
type: "none | sm | md | lg | full",
description: "The radius of the select.",
default: "-"
},
{
attribute: "placeholder",
type: "string",
description: "The placeholder of the select.",
default: "Select an option"
},
{
attribute: "labelPlacement",
type: "inside | outside | outside-left",
description: "The position of the label.",
default: "inside"
},
{
attribute: "label",
type: "ReactNode",
description: "The content to display as the label.",
default: "-"
},
{
attribute: "description",
type: "ReactNode",
description: "A description for the select. Provides a hint such as specific requirements for what to choose.",
default: "-"
},
{
attribute: "errorMessage",
type: "ReactNode | ((v: ValidationResult) => ReactNode)",
description: "An error message for the select.",
default: "-"
},
{
attribute: "startContent",
type: "ReactNode",
description: "Element to be rendered in the left side of the select.",
default: "-"
},
{
attribute: "endContent",
type: "ReactNode",
description: "Element to be rendered in the right side of the select.",
default: "-"
},
{
attribute: "selectorIcon",
type: "ReactNode",
description: "Element to be rendered as the selector icon.",
default: "-"
},
{
attribute: "scrollRef",
type: "React.RefObject<HTMLElement>",
description: "A ref to the scrollable element.",
default: "-"
},
{
attribute: "spinnerRef",
type: "React.RefObject<HTMLElement>",
description: "A ref to the spinner element.",
default: "-"
},
{
attribute: "maxListboxHeight",
type: "number",
description: "The maximum height of the listbox in pixels. Required when using virtualization.",
default: "256"
},
{
attribute: "itemHeight",
type: "number",
description: "The fixed height of each item in pixels. Required when using virtualization.",
default: "32"
},
{
attribute: "isVirtualized",
type: "boolean",
description: "Whether to enable virtualization. By default, it's enabled when the number of items exceeds 50.",
default: "undefined"
},
{
attribute: "fullWidth",
type: "boolean",
description: "Whether the select should take up the width of its parent.",
default: "true"
},
{
attribute: "isOpen",
type: "boolean",
description: "Whether the select is open by default (controlled).",
default: "-"
},
{
attribute: "defaultOpen",
type: "boolean",
description: "Whether the select is open by default (uncontrolled).",
default: "-"
},
{
attribute: "isRequired",
type: "boolean",
description: "Whether user select is required on the select before form submission.",
default: "false"
},
{
attribute: "isDisabled",
type: "boolean",
description: "Whether the select is disabled.",
default: "false"
},
{
attribute: "isMultiline",
type: "boolean",
description: "Whether the select should allow multiple lines of text.",
default: "false"
},
{
attribute: "isInvalid",
type: "boolean",
description: "Whether the select is invalid.",
default: "false"
},
{
attribute: "validationState",
type: "valid | invalid",
description: "Whether the select should display its \"valid\" or \"invalid\" visual styling. (Deprecated) use isInvalid instead.",
default: "-"
},
{
attribute: "showScrollIndicators",
type: "boolean",
description: "Whether the select should show scroll indicators when the listbox is scrollable.",
default: "true"
},
{
attribute: "autoFocus",
type: "boolean",
description: "Whether the select should be focused on the first mount.",
default: "false"
},
{
attribute: "disallowEmptySelection",
type: "boolean",
description: "Whether the collection allows empty selection.",
default: "false"
},
{
attribute: "disableAnimation",
type: "boolean",
description: "Whether the select should be animated.",
default: "true"
},
{
attribute: "disableSelectorIconRotation",
type: "boolean",
description: "Whether the select should disable the rotation of the selector icon.",
default: "false"
},
{
attribute: "hideEmptyContent",
type: "boolean",
description: "Whether the listbox will be prevented from opening when there are no items.",
default: "false"
},
{
attribute: "popoverProps",
type: "PopoverProps",
description: "Props to be passed to the popover component.",
default: "-"
},
{
attribute: "listboxProps",
type: "ListboxProps",
description: "Props to be passed to the listbox component.",
default: "-"
},
{
attribute: "scrollShadowProps",
type: "ScrollShadowProps",
description: "Props to be passed to the scroll shadow component.",
default: "-"
},
{
attribute: "classNames",
type: "Partial<Record<\"base\" \"label\" \"trigger\" \"mainWrapper\" \"innerWrapper\" \"selectorIcon\" \"value\" \"listboxWrapper\" \"listbox\" \"popoverContent\" \"helperWrapper\" \"description\" \"errorMessage\", string>>",
description: "Allows to set custom class names for the Select slots.",
default: "-"
}
]}
/>
### Select Events
<APITable
data={[
{
attribute: "onClose",
type: "() => void",
description: "Callback fired when the select popover is closed.",
default: "-"
},
{
attribute: "onOpenChange",
type: "(isOpen: boolean) => void",
description: "Callback fired when the select popover is opened or closed.",
default: "-"
},
{
attribute: "onSelectionChange",
type: "(keys: \"all\" | Set<React.Key> & {anchorKey?: string; currentKey?: string}) => void",
description: "Callback fired when the selected keys change.",
default: "-"
},
{
attribute: "onChange",
type: "React.ChangeEvent<HTMLSelectElement>",
description: "Native select change event, fired when the selected value changes.",
default: "-"
},
{
attribute: "renderValue",
type: "RenderValueFunction",
description: "Function to render the value of the select. It renders the selected item by default.",
default: "-"
}
]}
/>
---
### SelectItem Props
Check the [ListboxItem](/docs/components/listbox#listboxitem-props) props.
### SelectItem Events
Check the [ListboxItem](/docs/components/listbox#listboxitem-events) events.
### SelectSection Props
Check the [ListboxSection](/docs/components/listbox#listboxsection-props) props.
---
### Types
#### Render Value Function
The `T` type is the type of the data passed to the select `items`.
```tsx
export type SelectedItemProps<T> = {
/** A unique key for the item. */
key?: Key;
/** The props passed to the item. */
props?: Record<string, any>;
/** The item data. */
data?: T | null;
/** An accessibility label for this item. */
"aria-label"?: string;
/** The rendered contents of this item (e.g. JSX). */
rendered?: ReactNode;
/** A string value for this item, used for features like typeahead. */
textValue?: string;
/** The type of item this item represents. */
type?: string;
};
type SelectedItems<T> = Array<SelectedItemProps<T>>;
renderValue: (items: SelectedItems<T>) => ReactNode;
```