Selecting a value from a dropdown list of options.
The component can be imported via:
To use the Select component, pass in the following props:
label - a string label to render above the select
ariaLabelledby or ariaLabel may be usedvalue - the string value of the currently selected optiononChange - a callback to set the value stateoptions - an array of objects to render in the select dropdown menu. The objects should contain the following keys:
value - a string value, which corresponds to the value proplabel - a string text label to render in the dropdown menudescription - optional, a string with additional descriptive text to display below the labelsearchLabel - optional, a string used for searching the options (if different than the label)icon - a PlumaIcon name to render next to the optionisDisabled - optional, whether this options should be disabled (not selectable)tooltip - optional, a string tooltip to show on hoverwithTruncatedTooltip - optional, automatically show a tooltip when the label is truncatedplaceholder - a placeholder value to show when no option is selectedicon - optional, a PlumaIcon name to render on the left side of the select triggerA Select component can be used to select multiple values, which can be enabled
via the isMulti prop.
When isMulti is true, there are two important differences from the single value case:
value prop is an array of strings (the values of the selected options)onChange callback is called with an array of stringsYou can add descriptive text below an option's label:
Options carry their own elementBefore and elementAfter. elementBefore renders in
the option's leading slot, after the option icon if one is set; elementAfter renders
in the trailing slot, beside the checkmark shown when the option is selected — so a
selected and an unselected row still line up.
The select trigger takes the same two props: elementBefore renders after the trigger's
icon and before the selected value, and elementAfter renders after the clear button
and before the chevron.
In React both are nodes. In Ember, an option's slots are components (options are plain
data, so they can't take a block), while the trigger also accepts :elementBefore and
:elementAfter named blocks.
unsafe_leftSectionComponent on the trigger is deprecated. It still works and renders in
the same place, but elementBefore is the supported equivalent — and in Ember it can
also be filled with a named block.
Individual options can be disabled:
Enabling isSearchable on the component will render a search input inside the
select dropdown, which allows users to filter the options:
When using a searchable Select, you can allow users to create new options when their search doesn't match any existing options. This is useful for fields like tags, categories, or any user-defined values where the list of options should be dynamic.
Enable this feature with the withCreate and onCreate props:
withCreate - enables the "Create new" option when no exact match is foundonCreate - callback that receives the search value when "Create new" is selectedcreateOptionLabel - optional custom label for the "Create new" option (default: Create "{value}")
canCreateNewOption - optional function to control when the "Create new" option appears
The label shown for the "Create new" option can be customized using the createOptionLabel prop.
It accepts a string or a function that receives the search value:
The canCreateNewOption callback can be used to control whether specific values
should show/not show the "Create new" option, for example if we want to prevent
creation of items that have special names.
You can pass in groups of options to the selects to create breaks between sets of options:
An entire group can be disabled by setting its option entry to isDisabled:
The Select component supports making groups collapsible - where clicking
on the group's header will collapse/expand the group. This is enabled by
including an isCollapsible key on the group's object - when set to true,
that group will be collapsible.
Additionally, the defaultIsCollapsed key can be used to make a group collapsed
by default when first opening the dropdown.
For very long lists of options (hundreds, thousands of items), rendering the dropdown content may become very slow. For these cases, the Select component supports rendering "virtualized" lists, i.e. it will only render a small subset of options within the dropdown, and will update accordingly as users scroll through it.
For virtualization we use the TanStack Virtual library (both in Ember and React).
This option is disabled by default, and can be enabled via the withVirtualizer argument.
Optionally, the virtualizerOptions argument accepts the same options as the library's
Virtualizer class,
allowing you to customize the behavior if necessary.
The Select component accepts valueComponent, optionItemComponent, and optionComponent props,
which can be used to customize how the selected value, and the options in the dropdown,
are rendered.
To use them, you will need to pass in a component as the prop.
valueComponent is the component that will be used to render the selected value
in the Select's trigger. It will be called with the following props:
option - the option object from the options arrayoptionItemComponent is the component that renders each option button in the dropdown.
This needs to be customized carefully, as this is the component to which various
events and props are attached, to support features like keyboard navigation, focus management,
and accessibility via aria props.
Pluma also exports a SelectMenuOptionButton component, which is the one used
internally for these options. When customizing, it is recommended to use this component
as a base to make sure the basic styling and behavior is preserved.
This component is called with:
option - the option object from the options arrayisSelected - whether this is the currently selected option (the value matches this option)isActive - whether this is the currently active option (it's currently hovered or focused via keyboard)isDisabled - whether this option is disabledindex - the index of this option in the listlevel - the nesting level of this optioncollapsibleParentsCount - the number of collapsible parent groups (this, and the one below, are used internally
to apply the correct padding to nested options))nonCollapsibleParentsCount - the number of non-collapsible parent groupslabelRef - (only in React) a ref to an element inside the option that could become
truncated - this is used to detect when an option is truncated to optionally show a tooltipsetLabelElement - (only in Ember) like above, but for Ember - a callback to set the
potentially truncated elementoptionComponent - the component that renders this element's contentsoptionComponent is the content rendered inside the option button (inside optionItemComponent).
It will be called with the following props:
option - the option object from the options arrayisSelected - whether this is the currently selected option (the value matches this option)isActive - whether this is the currently active option (it's currently hovered or focused via keyboard)labelRef - (only in React) a ref to an element inside the option that could become
truncated - this is used to detect when an option is truncated to optionally show a tooltipsetLabelElement - (only in Ember) like above, but for Ember - a callback to set the
potentially truncated element
If you intend to enable withTruncatedTooltip on a customized optionComponent,
it is required to set the labelRef (in React) or call setLabelElement (in Ember)
with the element that might get truncated - otherwise the tooltip won't be able
to detect the truncation.The same component may be used for both valueComponent and optionComponent.
For example, we might define a custom option component like this:
Which could then be used in the Select component:
You may find that customizing the entire option may be too heavy-handed. Instead, you may just need to customize the icon area that prefixes the option.
For such cases, the Select component also accepts an iconComponent prop,
which can be used to customize how the icons next to the options are rendered.
To use iconComponent, you will need to pass in a component as the prop.
iconComponent will be called with the following props:
name - the name of the icon (this value will be the same as icon on the option)isSelected - whether the option this icon is a part of is currently selectedsize - the size of the control (which will either be sm or md, as per the size prop)For example, we might define a custom icon component like this:
Which could then be used in the Select component:
Individual options can display a tooltip on hover by setting the tooltip property
on the option object.
The tooltip placement can be customized with tooltipPlacement
(defaults to 'right'), though it's recommended to avoid using top or bottom
placements, as they would overlap with other options in the dropdown and make it
more difficult to navigate.
When options may have long labels that get truncated by CSS, setting withTruncatedTooltip
to true on the option will automatically show a tooltip only when the label text is
actually truncated. The tooltip content defaults to the option's label, but can be
overridden by also setting tooltip.
When you need tooltips with rich content like links or formatted text, use the
optionItemComponent prop
to wrap the default SelectMenuOptionButton with a custom Tooltip.
The custom optionItemComponent must be a forwardRef component.
When using a custom tooltip, you will also need to combine the external ref with
the tooltip's reference using a utility like useMergeRefs.
The Select component supports loading options data asynchronously. It will display
a loading spinner inside the input when isLoading is true. When performing
filtering through an API, you will also have to control the search input state
via the searchInputValue and onSearchInput props (otherwise the component
will perform filtering internally).
Select also supports infinite scrolling (for paginated APIs), and will call
the onLoadMore callback when scrolling towards the end of the list. The other
props expected for this use case are:
shouldLoadMore - a boolean indicating whether the onLoadMore callback should
be called at all (e.g. when there is a next page of data). Set to false if there
are no more pages/there is no more data to fetchisLoadingMore - whether more data is currently being fetched. While the API call
issued via the onLoadMore callback is running, this should be true, and will
show a loading spinner at the bottom of the listNote: It is up to you to merge the results of the paginated API calls into one
options array, which you pass into the component. The component itself only issues
callbacks when required, but you control the state.
The example below shows a Select that simulates an API with filtering and pagination.
The dropdown is rendered by SelectPalette, which adds four optional regions around the options list: a header (title, meta slot, close button and the search input), a categories rail on the left, a detail pane on the right that follows the highlighted option, and a footer.
Every region is off unless you configure it, so a Select with none of these props renders exactly what it always did.
| Region | Props |
|---|---|
| Header | paletteTitle, paletteHeaderMetaComponent, withPaletteCloseButton, onPaletteClose, paletteHeaderComponent |
| Categories rail | paletteCategories, activePaletteCategoryId, defaultActivePaletteCategoryId, onPaletteCategoryChange, paletteCategoriesComponent |
| Detail pane | paletteDetailComponent |
| Footer | paletteFooterComponent |
Two things worth knowing before you reach for these:
options that already match.paletteFooterComponent takes precedence over footerComponent when both are
set.See the SelectPalette docs for the region context shapes, how to replace a region's contents, and how to open a palette from a plain button.
PlumaProvider configurationBy default, the Select component enters and exits with a CSS transition animation (with a duration of 125ms).
The value can be overridden globally with the componentConfig prop on the PlumaProvider component:
This can be used, for example, in tests, to set the transition time to 0.
If a string label is not sufficient, a component can be used to render the label.
In React, the label prop accepts any ReactNode.
If a string description is not sufficient, a component can be used to render the description.
In React, the description prop accepts any ReactNode.
If a string error is not sufficient, a component can be used to render the error.
In React, the error prop accepts any ReactNode.
PlumaSelect accepts an isClearable prop, which renders a "clear" button
when it has a value. When the button is clicked, the onChange callback is called
with null.
PlumaSelect extends BoxWhich category is active. Use this for controlled mode.
Additional middleware to apply to Floating UI, for example custom middleware for more control. These will be applied after the default middleware.
Element (or elements) that describe the form field. Also used for error messages, as aria-errormessage isn't supported in all assistive technologies: https://a11ysupport.io/tech/aria/aria-errormessage_attribute
String value that labels the form field.
Element (or elements) that label the form field.
The component to render at the header of the menu (after the search input, if present, but before the options list).
Whether a new option with the given value can be created. This can be used to exclude certain values from showing a "Create new" option.
Default:false
Whether the Select can wrap selected values to multiple lines (instead of truncating overflow).
Only applies when isMulti is true.
Default:'Create "{value}"'
The label to show for the "Create new" option. If a function is provided, it will be called with the current search input value.
Which category starts out active, for uncontrolled mode. Uncontrolled
selection resets when the dropdown closes; use activePaletteCategoryId to
keep it across opens.
Whether the select dropdown is open initially (when uncontrolled).
A description for the field, providing additional context or hints.
Custom content to render in the trailing (after) slot of the select trigger,
after the clear button and before the chevron. In React this is a node; in
Ember it can be passed as a component or provided via the :elementAfter
named block.
Custom content to render in the leading (before) slot of the select trigger,
after the icon if one is set and before the selected value. Useful for
leading content the icon prop can't express, such as always-included tokens
or a custom badge. In React this is a node; in Ember it can be passed as a
component or provided via the :elementBefore named block.
An error message associated with the form field.
Boolean: Ember-only. If the error named block is used,
this prop can be set to true to indicate an error is present,
which will make the error block render.
The component to render at the header of the menu (before the search input, if present).
The name of an icon (from Pluma Icons) to be rendered on the left side of the select trigger.
The component to render for an option's icon.
An optional id to be assigned to the input element. Also used to generate ids for labels and error messages. If one isn't provided, it will be generated.
Whether the select is clearable.
When the clear button is clicked, the onChange callback
is called with null.
Whether the form field is disabled.
Whether the form field is in an invalid state.
Whether the component should show a loading spinner.
Whether more options are currently being loaded.
Whether multiple options can be selected.
Whether the select dropdown is open. Use this for controlled mode.
Whether the select options should be searchable (via an input inside the options menu).
The label text to show with the form field.
The name attribute is used for HTML forms
Called when the active (currently highlighted, but not necessarily selected) option changes when the dropdown is open.
The callback for when a value is selected.
When isMulti is true, the value in the callback
will be an array of the selected values.
Called when the dropdown finishes closing (after the close animation finishes).
Called when the "Create new" option is selected.
Note: onChange is not called when creating a new option: setting the new value
should also happen within this callback.
Callback for when the end of the dropdown is reached and more options should be loaded.
Called when the dropdown is opened.
Callback when the open state should change.
Called when a category in the rail is selected.
Called when the menu header's close button is pressed. Defaults to closing the dropdown.
A handler for the input event on the search text field.
If provided, the Select component won't handle the filtering itself,
and it's up to the developer to filter the options.
The component to render for each option in the menu.
An array of options to render.
Categories to render in a rail to the left of the options. Absent or empty
means no rail, unless paletteCategoriesComponent is set.
Selecting a category does not filter the options — pass options that already
match the active category.
The component to render as the rail's contents, replacing the default category
rows. Receives SelectPaletteCategoriesContext. Renders inside the rail.
The component to render in a detail pane to the right of the options, which
follows the highlighted option. Absent means no detail pane. Receives
SelectPaletteDetailContext. Renders inside the pane.
The component to render as the menu header's contents, replacing the title,
meta slot, close button and search input. Receives
SelectPaletteHeaderContext, which carries a ready-to-render search input.
Renders inside the header's padding.
The component to render in the menu header's trailing slot, after the title and before the close button. For content like a total item count.
A title to render in the menu's header, before the meta slot and the close button. The header is the padded band the search input lives in; a searchable Select with no title looks exactly as it did before.
Placeholder text to show inside the field.
Default:'Search'
An aria-label to apply to the search field (as it doesn't render with its own label).
A value for the search input. If provided, combine it with onSearchInput to
control filtering of the options.
Default:'Search'
Placeholder text to show inside the search field (if enabled).
Whether to allow browser autofill and password managers. When false (default), adds attributes to disable password managers (1Password, LastPass, Bitwarden, Dashlane). Set to true for fields where autofill is desired (e.g., email, password, address fields).
Whether the generated description id should be included in the input's aria-describedby attribute.
Disable this when a label`` tag surrounds the input as well as it's description text, for example in an OptionCard` component.
Whether the generated label id should be included in the input's aria-labelledby attribute.
Disable this when the label tag surrounds the input as well as it's label text,
for example in an OptionCard component.
Whether the onLoadMore callback should be called when the end of the dropdown is reached. If no more paginated options are available, this should be set to false.
By default, form elements will throw an error if no label or aria-label is provided. Disable this to suppress the error, for example when building a custom form element, and you want to handle labeling yourself.
The size of the input element. medium and small are deprecated, use md and sm instead.
Additional classes to be applied to the description element.
Additional classes to be applied to the error element.
Additional classes to be applied to the field node.
Ember-only: this is used internally to detect whether a label
named block is present, and to ignore empty label props if so.
The initial value inside the search input. Only used in snapshots/tests.
Additional classes to be applied to the input node.
Sets the size attribute on the input element. This controls the visible width of the input in characters.
Legacy compatibility styles will be removed in the next major version. Components already render in the target style by default.
Whether the component should render with "legacy" styles.
Additional classes to be applied to the label node.
Use elementBefore instead. It takes the same content and renders in the same place, and in Ember it can also be provided as an :elementBefore named block.
A component to render on the left side within the select trigger, before
the selected value (and after the icon, if set). Useful for leading
content the icon prop can't express, such as always-included tokens or a
custom badge.
Additional classes to apply on the SelectPopover component (the floating element, not the trigger).
Additional size configuration for the SelectPopover component. Can be used to e.g. restrict the maxWidth of the popover. If the value of one of the keys is a function, it will be called with the default values that PlumaSelect would use (with numbers representing pixel values).
Additional inline styles to apply on the SelectPopover component (the floating element, not the trigger).
The current value. Can be either:
value of the selected option itemoptions array
When isMulti is true, the value is an array of the
above value options.The component to render for the selected value.
Options, or a function that returns options, for the Virtualizer.
Whether to apply the center-baseline variant.
When the Select is searchable, whether it should be possible to create new options if the search input doesn't exactly match any existing options.
Whether to render a close button in the menu's header.
Show tooltip only when label is truncated, for all options and group headers. Can be overridden per-option or per-group.
Whether the menu options should render in a virtualized list.