Virtualized Select This component is still under testing, if you found any bug or issue please report it at GitHub for us to fix.
Background
In some use-cases, a single selector may end up loading tens of thousands of rows of data.
Rendering that much data into the DOM could be a burden to the browser, which can result in performance issues.
For a better user and developer experience, we decided to add this component.
Basic usage
The simplest selector
export function SelectV2BasicUsage() { const [value, setValue] = useState<string>(); return ( <ElSelectV2 value={value} options={baseOptions} placeholder="Please select" style={{ width: 240 }} onChange={(next) => setValue(next as string)} /> ); } Multi select
The basic multi-select selector with tags
export function SelectV2Multiple() { const [value, setValue] = useState<string[]>([]); return ( <ElSelectV2 value={value} options={baseOptions} placeholder="Please select" style={{ width: 240 }} multiple onChange={(next) => setValue(asArray(next))} /> ); } Sizes Add size attribute to change the size of Select-V2. In addition to the default size, there are two other options: large, small.
export function SelectV2Size() { const [value, setValue] = useState<string>(); return ( <div style={row}> <ElSelectV2 value={value} options={baseOptions} placeholder="Please select" size="large" style={{ width: 240 }} onChange={(next) => setValue(next as string)} /> <ElSelectV2 value={value} options={baseOptions} placeholder="Please select" style={{ width: 240 }} onChange={(next) => setValue(next as string)} /> <ElSelectV2 value={value} options={baseOptions} placeholder="Please select" size="small" style={{ width: 240 }} onChange={(next) => setValue(next as string)} /> </div> ); }
You can collapse tags to a text by using collapse-tags attribute. You can check them when mouse hover collapse text by using collapse-tags-tooltip attribute.
use collapse-tags-tooltip
export function SelectV2HideExtraTags() { const [value, setValue] = useState<string[]>([]); const [value2, setValue2] = useState<string[]>([]); const [value3, setValue3] = useState<string[]>([]); return ( <> <div style={{ margin: 16 }}> <p>use collapse-tags</p> <ElSelectV2 value={value} options={baseOptions} placeholder="Please select" style={{ width: 240 }} multiple collapseTags onChange={(next) => setValue(asArray(next))} /> </div> <div style={{ margin: 16 }}> <p>use collapse-tags-tooltip</p> <ElSelectV2 value={value2} options={baseOptions} placeholder="Please select" style={{ width: 240 }} multiple collapseTags collapseTagsTooltip onChange={(next) => setValue2(asArray(next))} /> </div> <div style={{ margin: 16 }}> <p>use max-collapse-tags</p> <ElSelectV2 value={value3} options={baseOptions} placeholder="Please select" style={{ width: 240 }} multiple collapseTags collapseTagsTooltip maxCollapseTags={3} onChange={(next) => setValue3(asArray(next))} /> </div> </> ); } Filterable multi-select
When the options are overwhelmingly too many, you can use filterable option to enable filter feature for finding out the desired option
export function SelectV2Filterable() { const [value, setValue] = useState<string[]>([]); return ( <ElSelectV2 value={value} options={baseOptions} placeholder="Please select" style={{ width: 240 }} multiple filterable onChange={(next) => setValue(asArray(next))} /> ); } Disabled selector and select options
You can choose to disable selector itself or the option.
export function SelectV2Disabled() { const [value, setValue] = useState<string[]>([]); const options = Array.from({ length: 1000 }).map((_, idx) => ({ value: `Option${idx + 1}`, label: `${initials[idx % 10]}${idx}`, disabled: idx % 10 === 0, })); return ( <> <ElSelectV2 value={value} filterable options={options} placeholder="Please select" style={{ width: 240, marginRight: 16, verticalAlign: "middle" }} multiple onChange={(next) => setValue(asArray(next))} /> <ElSelectV2 value={value} disabled filterable options={options} placeholder="Please select" style={{ width: 240, verticalAlign: "middle" }} multiple onChange={(next) => setValue(asArray(next))} /> </> ); } Option Grouping
We can group option as we wanted, as long as the data satisfies the pattern.
export function SelectV2Grouping() { const [value, setValue] = useState<string[]>([]); const options: SelectV2OptionType[] = Array.from({ length: 10 }).map( (_, idx) => { const label = idx + 1; return { value: `Group ${label}`, label: `Group ${label}`, options: Array.from({ length: 10 }).map((__, childIdx) => ({ value: `Option ${childIdx + 1 + 10 * label}`, label: `${initials[childIdx % 10]}${childIdx + 1 + 10 * label}`, })), }; }, ); return ( <ElSelectV2 value={value} options={options} placeholder="Please select" style={{ width: 240 }} multiple filterable onChange={(next) => setValue(asArray(next))} /> ); } Clearable selector
We can clear all the selected options at once, also applicable for single select.
export function SelectV2Clearable() { const [value1, setValue1] = useState<string[]>([]); const [value2, setValue2] = useState<string>(); return ( <> <ElSelectV2 value={value1} options={baseOptions} placeholder="Please select" style={{ width: 240, marginRight: 16, verticalAlign: "middle" }} multiple clearable onChange={(next) => setValue1(asArray(next))} /> <ElSelectV2 value={value2} options={baseOptions} placeholder="Please select" style={{ width: 240, verticalAlign: "middle" }} clearable onChange={(next) => setValue2(next as string)} /> </> ); } Customized option renderer
We can define our own template for rendering the option in the popup.
export function SelectV2CustomizedOption() { const [value, setValue] = useState<string[]>([]); return ( <ElSelectV2 value={value} filterable options={baseOptions} placeholder="Please select" style={{ width: 240 }} multiple onChange={(next) => setValue(asArray(next))} > {({ item }) => ( <> <span style={{ marginRight: 8 }}>{String(item.label)}</span> <span style={{ color: "var(--el-text-color-secondary)", fontSize: 13, }} > {String(item.value)} </span> </> )} </ElSelectV2> ); }
You can customize the header of the dropdown.
Use slot to customize the content.
export function SelectV2CustomHeader() { const [value, setValue] = useState<string[]>([]); const checkAll = value.length === baseOptions.length; const indeterminate = value.length > 0 && !checkAll; return ( <ElSelectV2 value={value} options={baseOptions} multiple clearable collapseTags placeholder="Select" popperClass="custom-header" maxCollapseTags={1} style={{ width: 240 }} onChange={(next) => setValue(asArray(next))} header={ <ElCheckbox checked={checkAll} indeterminate={indeterminate} onCheckedChange={(checked) => setValue(checked ? baseOptions.map((item) => item.value as string) : []) } > All </ElCheckbox> } /> ); }
You can customize the footer of the dropdown.
Use slot to customize the content.
export function SelectV2CustomFooter() { const selectRef = useRef<SelectV2Expose>(null); const [value, setValue] = useState<string[]>([]); const [options, setOptions] = useState(baseOptions); const [isAdding, setIsAdding] = useState(false); const [optionName, setOptionName] = useState(""); const clear = () => { setOptionName(""); setIsAdding(false); }; const onConfirm = () => { if (!optionName) return; const next = [...options, { label: optionName, value: optionName }]; setOptions(next); clear(); setTimeout(() => selectRef.current?.scrollTo(next.length - 1), 0); }; return ( <ElSelectV2 ref={selectRef} value={value} options={options} placeholder="Select" style={{ width: 240 }} onChange={(next) => setValue(asArray(next))} footer={ !isAdding ? ( <ElButton text bg size="small" onClick={() => setIsAdding(true)}> Add an option </ElButton> ) : ( <div style={{ ...column, gap: 8 }}> <ElInput value={optionName} placeholder="input option name" size="small" style={{ width: "100%" }} onChange={setOptionName} /> <div> <ElButton type="primary" size="small" onClick={onConfirm}> confirm </ElButton> <ElButton size="small" onClick={clear}>cancel</ElButton> </div> </div> ) } /> ); } Create Option
Create and select new items that are not included in select options
By using the allow-create attribute, users can create new items by typing in the input box. Note that for allow-create to work, filterable must be true. This example also demonstrates default-first-option. When this attribute is set to true, you can select the first option in the current option list by hitting enter without having to navigate with mouse or arrow keys.
It will be better to set :reserve-keyword="false" when use allow-create
set reserve-keyword false
export function SelectV2AllowCreate() { const [value1, setValue1] = useState<string[]>([]); const [value2, setValue2] = useState<string>(); const [value3, setValue3] = useState<string[]>([]); return ( <div style={{ flex: "auto" }}> <div> <ElSelectV2 value={value1} options={baseOptions} placeholder="Please select" style={{ width: 240, marginRight: 16, verticalAlign: "middle" }} allowCreate defaultFirstOption filterable multiple clearable onChange={(next) => setValue1(asArray(next))} /> <ElSelectV2 value={value2} options={baseOptions} placeholder="Please select" style={{ width: 240, verticalAlign: "middle" }} allowCreate defaultFirstOption filterable clearable onChange={(next) => setValue2(next as string)} /> </div> <div> <p style={{ marginTop: 20, marginBottom: 8 }}> set reserve-keyword false </p> <ElSelectV2 value={value3} options={baseOptions} placeholder="Please select" style={{ width: 240, marginRight: 16, verticalAlign: "middle" }} allowCreate defaultFirstOption filterable multiple clearable reserveKeyword={false} onChange={(next) => setValue3(asArray(next))} /> </div> </div> ); } Remote search
Enter keywords and search data from server.
Set the value of filterable and remote with true to enable remote search, and you should pass the remote-method. remote-method is a Function that gets called when the input value changes, and its parameter is the current input value.
export function SelectV2RemoteSearch() { const [value, setValue] = useState<string[]>([]); const [options, setOptions] = useState<typeof stateItems>([]); const [loading, setLoading] = useState(false); const remoteMethod = (query: string) => { if (query !== "") { setLoading(true); setTimeout(() => { setLoading(false); setOptions( stateItems.filter((item) => item.label.toLowerCase().includes(query.toLowerCase()), ), ); }, 200); } else { setOptions([]); } }; return ( <div style={wrap}> <div style={{ margin: 16 }}> <p style={{ marginTop: 0 }}>default</p> <ElSelectV2 value={value} style={{ width: 240 }} multiple filterable remote clearable options={options} loading={loading} placeholder="Please enter a keyword" remoteMethod={remoteMethod} onChange={(next) => setValue(asArray(next))} /> </div> <div style={{ margin: 16 }}> <p style={{ marginTop: 0 }}>use remote-show-suffix</p> <ElSelectV2 value={value} style={{ width: 240 }} multiple filterable remote remoteShowSuffix clearable options={options} loading={loading} placeholder="Please enter a keyword" remoteMethod={remoteMethod} onChange={(next) => setValue(asArray(next))} /> </div> </div> ); } Use value-key attribute
when options.value is an object, you should set a unique identity key name for value
::: tip
Before 2.4.0 , value-key was used both as the unique value of the selected object and as an alias for the value in options. Now value-key is only used as the unique value of the selected object, and the alias for the value in options is props.value.
:::
export function SelectV2UseValueKey() { const [value, setValue] = useState<Record<string, unknown>>({ name: "Option 1", test: "test 0", }); const options = Array.from({ length: 1000 }).map((_, idx) => ({ value: { name: `Option ${idx + 1}`, test: `test ${idx % 3}`, }, label: `${initials[idx % 10]}${idx}`, })); return ( <ElSelectV2 value={value} options={options} placeholder="Please select" valueKey="name" style={{ width: 240 }} onChange={(next) => setValue(next as Record<string, unknown>)} /> ); } Aliases for custom options 2.4.2
When your options format is different from the default format, you can customize the alias of the options through the props attribute
export function SelectV2PropsDemo() { const [value, setValue] = useState<string[]>([]); const options = Array.from({ length: 1000 }).map((_, idx) => ({ id: `Option ${idx + 1}`, name: `${initials[idx % 10]}${idx}`, })); return ( <ElSelectV2 value={value} options={options} props={{ label: "name", value: "id" }} placeholder="Please select" style={{ width: 240 }} filterable multiple onChange={(next) => setValue(asArray(next))} /> ); } Custom Tag 2.5.0
You can customize tags.
Insert customized tags into the slot of el-select. collapse-tags, collapse-tags-tooltip, max-collapse-tags will not work.
export function SelectV2CustomTag() { const colors = [ { value: "#E63415", label: "red" }, { value: "#FF6600", label: "orange" }, { value: "#FFDE0A", label: "yellow" }, { value: "#1EC79D", label: "green" }, { value: "#14CCCC", label: "cyan" }, { value: "#4167F0", label: "blue" }, { value: "#6222C9", label: "purple" }, ]; const [value, setValue] = useState<string[]>(colors.map((item) => item.value)); return ( <ElSelectV2 value={value} multiple placeholder="Select" options={colors} style={{ width: 240 }} onChange={(next) => setValue(asArray(next))} tag={({ data }) => ( <> {data.map((option) => ( <ElTag key={String(option.value)} color={String(option.value)} style={{ border: "none", aspectRatio: "1" }} /> ))} </> )} > {({ item }) => ( <div style={{ display: "flex", alignItems: "center" }}> <ElTag color={String(item.value)} size="small" style={{ marginRight: 8 }} /> <span style={{ color: String(item.value) }}> {String(item.label)} </span> </div> )} </ElSelectV2> ); } Custom Loading 2.5.2
Override loading content.
export function SelectV2CustomLoading() { const [value, setValue] = useState<string[]>([]); const [options, setOptions] = useState<typeof stateItems>([]); const [loading, setLoading] = useState(false); const remoteMethod = (query: string) => { if (query) { setLoading(true); setTimeout(() => { setLoading(false); setOptions( stateItems.filter((item) => item.label.toLowerCase().includes(query.toLowerCase()), ), ); }, 3000); } else { setOptions([]); } }; const loadingSlot = ( <ElIcon className="is-loading" size={20}> <Loading /> </ElIcon> ); return ( <div style={wrap}> <div style={{ margin: 16 }}> <p style={{ marginTop: 0 }}>loading icon1</p> <ElSelectV2 value={value} multiple filterable remote reserveKeyword placeholder="Please enter a keyword" remoteMethod={remoteMethod} loading={loading} options={options} style={{ width: 240 }} loadingSlot={loadingSlot} onChange={(next) => setValue(asArray(next))} /> </div> <div style={{ margin: 16 }}> <p style={{ marginTop: 0 }}>loading icon2</p> <ElSelectV2 value={value} multiple filterable remote reserveKeyword placeholder="Please enter a keyword" remoteMethod={remoteMethod} loading={loading} options={options} style={{ width: 240 }} loadingSlot={loadingSlot} onChange={(next) => setValue(asArray(next))} /> </div> </div> ); } Empty Values 2.7.0
If you want to support empty string, please set empty-values to [null, undefined].
If you want to change the clear value to null, please set value-on-clear to null.
export function SelectV2EmptyValues() { const [value, setValue] = useState<string | null>(""); const options = [ { value: "", label: "All" }, ...baseOptions, ]; return ( <ElSelectV2 value={value} options={options} emptyValues={[null, undefined]} valueOnClear={null} clearable placeholder="Select" style={{ width: 240 }} onChange={(next) => setValue(next as string | null)} onClear={() => message.info(`The clear value is: ${value}`)} /> ); } Custom Label 2.7.4
You can customize label.
export function SelectV2CustomLabel() { const [value1, setValue1] = useState<string>("Option 1"); const [value2, setValue2] = useState<string[]>(["Option 1"]); const label = ({ label: text, value }: { label: unknown; value: unknown }) => ( <> <span>{String(text)}: </span> <span style={{ fontWeight: "bold" }}>{String(value)}</span> </> ); return ( <div style={row}> <ElSelectV2 value={value1} options={baseOptions} placeholder="Select" style={{ width: 240 }} clearable label={label} onChange={(next) => setValue1(next as string)} /> <ElSelectV2 value={value2} options={baseOptions} placeholder="Select" style={{ width: 240 }} clearable multiple label={label} onChange={(next) => setValue2(asArray(next))} /> </div> ); } Custom Width 2.9.2
The width of dropdown box is calculated by default based on the value of label. If you customize the dropdown box options through the default slot, it is likely that the text displayed in the options is not equal to the value of label, resulting in calculation errors. In this case, you can set the fit-input-width attribute to a number to fix its width.
export function SelectV2CustomWidth() { const [value, setValue] = useState<string>(); const options = Array.from({ length: 1000 }).map((_, idx) => ({ value: `Option ${idx + 1}`, label: `${initials[idx % 10]}${idx}${"-".repeat(Math.ceil(idx / 25))}`, })); return ( <div style={row}> <ElSelectV2 value={value} options={options} placeholder="Please select" style={{ width: 240 }} fitInputWidth={false} onChange={(next) => setValue(next as string)} /> <ElSelectV2 value={value} options={options} placeholder="Please select" style={{ width: 240 }} fitInputWidth onChange={(next) => setValue(next as string)} /> <ElSelectV2 value={value} options={options} placeholder="Please select" style={{ width: 240 }} fitInputWidth={440} onChange={(next) => setValue(next as string)} > {({ item }) => <span>{`${String(item.value)}${String(item.label)}`}</span>} </ElSelectV2> </div> ); } API
Attributes
Name Description Type Default model-value / v-model binding value string / number / boolean / object / array— options data of the options, the key of value and label can be customize by props array— props 2.4.2 configuration options, see the following table object— multiple is multiple booleanfalse disabled is disabled booleanfalse value-key unique identity key name for value, required when value is an object stringvalue size size of component enum'' clearable whether select can be cleared booleanfalse clear-icon custom clear icon string / objectCircleClose collapse-tags whether to collapse tags to a text when multiple selecting booleanfalse multiple-limit maximum number of options user can select when multiple is true. No limit when set to 0 number0 id native input id input string— name the name attribute of select input string— effect tooltip theme, built-in theme: dark / light enum / stringlight autocomplete autocomplete of select input stringoff placeholder placeholder stringPlease select filterable whether Select is filterable booleanfalse allow-create whether creating new items is allowed. To use this, filterable must be true booleanfalse filter-method custom filter method, the first parameter is the current input value. To use this, filterable must be true method Function— loading whether Select is loading data from server booleanfalse loading-text displayed text while loading data from server, default is 'Loading' string— reserve-keyword whether reserve the keyword after select filtered option. booleantrue default-first-option select first matching option on enter key. Use with filterable or remote booleanfalse no-match-text displayed text when no data matches the filtering query, you can also use slot empty, default is 'No matching data' string— no-data-text displayed text when there is no options, you can also use slot empty stringNo Data popper-class custom class name for Select's dropdown and tags' tooltip string / object'' popper-style 2.11.0 custom style for Select's dropdown and tags' tooltip string / object— teleported whether select dropdown is teleported, if true it will be teleported to where append-to sets booleantrue append-to 2.8.8 which element the select dropdown appends to CSSSelector / HTMLElement— persistent when select dropdown is inactive and persistent is false, select dropdown will be destroyed booleantrue popper-options popper.js parametersobject refer to popper.js doc{} automatic-dropdown for non-filterable Select, this prop decides if the option menu pops up when the input is focused booleanfalse fit-input-width 2.9.2 whether the width of the dropdown is the same as the input, if the value is number, then the width is fixed boolean / numbertrue suffix-icon 2.9.8 custom suffix icon component string / objectArrowDown height The height of the dropdown panel, 34px for each item number274 item-height The height of the dropdown item number34 estimated-option-height Controls virtual-list sizing mode: if undefined, the list uses fixed item height from item-height; if provided, the list uses dynamic item sizing and this value as the estimated item height. number— scrollbar-always-on Controls whether the scrollbar is always displayed booleanfalse remote whether search data from server booleanfalse debounce 2.11.7 debounce delay during remote search, in milliseconds number300 remote-method function that gets called when the input value changes. Its parameter is the current input value. To use this, filterable must be true Function— remote-show-suffix 2.11.9 in remote search method show suffix icon booleanfalse validate-event whether to trigger form validation booleantrue offset 2.8.8 offset of the dropdown number12 show-arrow 2.8.8 whether the dropdown has an arrow booleantrue placement position of dropdown enumbottom-start fallback-placements 2.5.6 list of possible positions for dropdown popper.js array['bottom-start', 'top-start', 'right', 'left'] collapse-tags-tooltip 2.3.0 whether show all selected tags when mouse hover text of collapse-tags. To use this, collapse-tags must be true booleanfalse tag-tooltip 2.13.3 configuration object for the collapse-tags tooltip. To use this, collapse-tags and collapse-tags-tooltip must be true object{} max-collapse-tags 2.3.0 The max tags number to be shown. To use this, collapse-tags must be true number1 tag-type 2.5.0 tag type enuminfo tag-effect 2.7.7 tag effect enumlight aria-label a11y 2.5.0 same as aria-label in native input string— empty-values 2.7.0 empty values of component, see config-provider array— value-on-clear 2.7.0 clear return value, see config-provider string / number / boolean / Function— popper-append-to-body deprecated whether to append the popper menu to body. If the positioning of the popper is wrong, you can try to set this prop to false booleanfalse tabindex 2.9.0 tabindex for input string / number—
props
Attribute Description Type Default value specify which key of node object is used as the node's value stringvalue label specify which key of node object is used as the node's label stringlabel options specify which key of node object is used as the node's children stringoptions disabled specify which key of node object is used as the node's disabled stringdisabled
Properties in tag-tooltip follow this priority order:
Explicitly defined fields within the tag-tooltip object.
Shared props inherited from el-select-v2 (e.g. effect, popper-class, popper-style, teleported, append-to, popper-options).
Default values of the underlying el-tooltip component.
This allows you to override specific tooltip behaviors for tags while maintaining consistency with the Select dropdown by default.
Custom Container Positioning
When appending the Tooltip to a custom container (via the append-to attribute), the container should be configured with position: relative or position: absolute to ensure accurate positioning. Additionally, you can apply overflow: hidden to the container if you need to prevent the Tooltip from overflowing its boundaries.
Attribute Description Type Default append-to which element the tooltip CONTENT appends to CSSSelector / HTMLElement— placement position of Tooltip enumbottom fallback-placements list of possible positions for Tooltip popper.js array['bottom', 'top', 'right', 'left'] effect Tooltip theme, built-in theme: dark / light enum / string— popper-class custom class name for Tooltip's popper string— popper-style custom style for Tooltip's popper string / object— transition animation name string— teleported whether tooltip content is teleported, if true it will be teleported to where append-to sets boolean— popper-options popper.js parametersobject refer to popper.js doc— show-after delay of appearance, in millisecond number— hide-after delay of disappear, in millisecond number— auto-close timeout in milliseconds to hide tooltip number— offset offset of the Tooltip number—
Events
Name Description Type change triggers when the selected value changes, the param is current selected value Functionvisible-change triggers when the dropdown appears/disappears, the param will be true when it appears, and false otherwise Functionremove-tag triggers when a tag is removed in multiple mode, the param is removed tag value Functionclear triggers when the clear icon is clicked in a clearable Select Functionblur triggers when Input blurs Functionfocus triggers when Input focuses Functionend-reached 2.14.0 triggers when dropdown scroll reaches an end Function
Slots
Name Description Type default Option renderer — header 2.5.2 content at the top of the dropdown — footer 2.5.2 content at the bottom of the dropdown — empty content when options is empty — prefix prefix content of input — tag 2.5.0 content as Select tag, subTags data, selectDisabled and deleteTag introduced in 2.10.3 objectloading 2.5.2 content as Select loading — label 2.7.4 content as Select label. index introduced in 2.11.2 object
Exposes
Name Description Type focus focus the Input component Functionblur blur the Input component, and hide the dropdown FunctionselectedLabel 2.8.5 get the currently selected label object