Select

One value or several, chosen from a list the field opens. Search, grouping, tags, custom option rendering and virtual scrolling.

Usage

When to reach for Select rather than one of its neighbors, what each of its axes is for, and the decisions it leaves to you.

When to use

One value, or several, chosen from a list the field opens on demand. Select is the right control when the options are too many to show at rest, when the reader needs to search them, or when the set is loaded rather than written into the page.
It is also the control that carries the most weight in a form, so reaching for it out of habit is the common mistake. Four neighbors take the job off it:
  • Radio.Group when the list is short enough to show. Up to about five options, seeing all of them at once beats opening a popup to find out what they are.
  • AutoComplete when the value is whatever the reader typed and the list is only a suggestion. Select in mode="tags" also accepts free text, but it commits each entry as a tag; AutoComplete stays a single free text field.
  • Cascader or TreeSelect when the options are a hierarchy the reader navigates. Select's own grouping is one flat level of headings, not a tree.
  • Transfer when the reader is building a set rather than picking from one, and needs to see what is in and what is out side by side.

Single, multiple and tags

mode is the first decision and it changes more than the number of values. Left off, the control holds one value and its text sits in the field. multiple and tags both switch to a row of removable tags.
  • mode="multiple" picks several values from the list you supplied, and nothing else. This is the one to reach for by default.
  • mode="tags" also accepts free text: a typed value with no matching option becomes a tag on Enter. Pair it with tokenSeparators, which also splits pasted text, so pasting Lucy,Jack becomes two tags.
  • Both multi modes turn search on by default. A plain showSearch boolean is only needed in single mode.
  • Clearing sets the value to undefined. With allowClear, a single select calls onChange with undefined and a multiple one with [], so type your state to allow it: useState<string | undefined>().

Options, not children

Pass the list through options. Select.Option and Select.OptGroup are not exported, since Ant Design deprecates both.
  • An option is { value, label } plus anything else you want to carry. The extra fields are yours: search and render callbacks receive the whole object.
  • fieldNames renames the keys (value, label, groupLabel, options) so a payload that already calls them something else does not need mapping first.
  • labelInValue switches value and onChange to carry { value, label } pairs. Reach for it when a form needs the selected label as well as its id and has nowhere to look one up.
  • Type your options through the generics. Select<string, Contact> types the value and the option, so render and search callbacks see your extra fields on option.data. Without them the option type falls back to any.
  • Clear a dependent value yourself. When one select's options depend on another's value, reset the second when the first changes, or a value from the old list stays selected. For more than two levels, use Cascader.
Put search options under showSearch. filterOption, filterSort, optionFilterProp, onSearch, searchValue and autoClearSearchValue are deprecated as top-level props. Pass them in the object form, showSearch={{ filterOption }}, which also turns search on.
  • optionFilterProp names the field to match against. It also takes an array, so one prop can search a label and an email.
  • A function filterOption replaces the default match entirely and receives the typed string with the whole option.
  • Turn off filtering for server results. When onSearch fetches the options, set filterOption: false so the list is not filtered a second time. Otherwise rows the server matched on fields the client cannot see disappear.
  • filterSort orders what survives the filter, and it is given the current search value, so exact prefixes can be lifted to the top.

Rendering labels, options and tags

Three render props, each for a different surface. Use them when a string is not enough. Each one replaces the plain text a screen reader would otherwise have read.
  • labelRender draws the selected value in the field. A value with no matching option (a stale id from a previous release) shows as the raw value by default, and labelRender can turn it into something a person can read.
  • optionRender draws a row in the popup, and receives the option with its index. A second line, an avatar or a secondary field belongs here. Raise listItemHeight to match a taller row.
  • tagRender replaces the multiple-mode tag. It receives closable and onClose, and passing both on keeps the remove button working.
  • popupRender wraps the whole menu, for a footer action such as "Add new" under the list. If it holds an input, call e.stopPropagation() in its onKeyDown, or Space selects the active option while you type. dropdownRender is its deprecated name.
Grouping is data rather than a render prop: give an entry a label and its own nested options array and it renders as a heading. It is one flat level. For a real hierarchy, use TreeSelect.

Clearing, capping and collapsing

  • allowClear adds the clear button. Its object form takes a clearIcon and a label, and the label is worth passing: it defaults to the literal string "Clear" rather than to a locale entry.
  • maxCount caps how many values the control holds. Once it is reached, the other options cannot be picked.
  • maxTagCount only changes the display: it folds the tags past its limit into one + N ... tag, which maxTagPlaceholder can relabel.
  • maxTagCount="responsive" sizes that collapse to the control's width. With few enough tags to fit it can show a + 0 ... tag, so prefer a number where the width is known.
  • maxTagTextLength truncates a single tag's text. It is a last resort: a truncated label the reader cannot expand is a label they cannot check.

Loading and remote data

A remote-backed Select is three props working together: showSearch.onSearch to fire the request, loading to show it is in flight and notFoundContent to say what the empty popup means.
  • notFoundContent should say something different before the first search than after one that matched nothing. "Type to search" and "No matches" are two different states wearing the same empty list.
  • Debounce the handler. Without it a real backend sees one call per keystroke rather than one per pause.
  • Guard against an out-of-order response with a request id. A later keystroke's promise can resolve before an earlier one's, and the stale result then overwrites the fresh one.
  • Keep the currently selected value in the options list even when it falls outside the latest search result, or reach for labelRender. Otherwise the field empties itself the moment the reader types.

Virtual scrolling

The popup virtualizes by default: only the rows near the viewport exist in the DOM, however many options you pass. A thousand-option list costs what a ten-option one costs. Leave it on.
  • virtual={false} renders every option node at once. It is worth it only for a list short enough that the DOM cost is nothing and something in a row needs to be found by the browser: in-page find, a print stylesheet, an automated test that queries every option.
  • popupMatchSelectWidth={false} also turns virtualization off. On a long list, keep the popup the width of the field.
  • listHeight (256 by default) and listItemHeight (the theme's controlHeight) size the list. A row drawn taller than listItemHeight through optionRender makes the scrollbar wrong, so raise it when you raise the row.
  • Virtual scrolling has an accessibility cost as well as a performance win. See the Accessibility tab before turning it on for a list a reader has to browse rather than search.
Style the popup with classNames.popup. The popup renders in a portal, outside the field, so a [&_.ant-select-item] selector on the field never reaches it. classNames.popup takes root, list and listItem.
  • The popup follows your theme wherever it renders, so set getPopupContainer only when it has to scroll or clip with its field.
  • Use the new names: popupClassName is now classNames.popup.root, dropdownStyle is styles.popup, onDropdownVisibleChange is onOpenChange and dropdownMatchSelectWidth is popupMatchSelectWidth.

Variant, status and size

Select uses Ant Design's own props for all three, with no emphasis of its own.
  • variant is the fill and border axis (outlined, filled, borderless, underlined). Pick one per form and keep it. It replaces the deprecated bordered.
  • status takes error or warning and colors the border, and nothing else. The message that explains the error belongs to Form.Item.
  • size is density, and it should follow the form around it rather than be set per field. ConfigProvider's componentSize is the place to set it once.
  • disabled is the only lock Select has. There is no readOnly prop, so a value that must be visible but not changeable is better rendered as text than as a disabled field.