Select

Custom searchable select
Import

Usage

Select component allows user to pick one option from the given data, use this component when you need to create custom value selection which is not possible with NativeSelect:

Bare minimum usage example:

import { Select } from '@mantine/core';
function Demo() {
return (
<Select
label="Your favorite framework/library"
placeholder="Pick one"
data={[
{ value: 'react', label: 'React' },
{ value: 'ng', label: 'Angular' },
{ value: 'svelte', label: 'Svelte' },
{ value: 'vue', label: 'Vue' },
]}
/>
);
}

Controlled

import { useState } from 'react';
import { Select } from '@mantine/core';
function Demo() {
const [value, setValue] = useState('');
return <Select value={value} onChange={setValue} data={[]} />;
}

Note that Select value should always be either string or null:

// Incorrect, will have bugs
<Select data={[{ value: 1, label: '1' }]} value={1} />
// Correct, works as expected
<Select data={[{ value: '1', label: '1' }]} value="1" />

Data prop

Select support two different data formats:

  1. An array of strings – use when you do not need to customize item component or display label different than value.
  2. An array of objects with required value and label properties and any other additional properties.
// Data as an array of strings, will be mapped to
// [
// { value: 'React', label: 'React' },
// { value: 'Angular', label: 'Angular' },
// { value: 'Svelte', label: 'Svelte' },
// { value: 'Vue', label: 'Vue' },
// ]
<Select data={['React', 'Angular', 'Svelte', 'Vue']} />
// Data as an array of objects:
// Minimal example (same as first example above)
<Select data={[
{ value: 'React', label: 'React' },
{ value: 'Angular', label: 'Angular' },
{ value: 'Svelte', label: 'Svelte' },
{ value: 'Vue', label: 'Vue' },
]} />
// Additional data properties for custom item component (see documentation below)
<Select
itemComponent={({ value, label, image, name }) => /* Your custom item component with data properties */}
data={[
{
value: 'bob@handsome.inc',
label: 'bob@handsome.inc',
image: 'image-link',
name: 'Bob Handsome',
},
{
value: 'bill@outlook.com',
label: 'bill@outlook.com',
image: 'image-link',
name: 'Bill Rataconda',
},
{
value: 'amy@wong.cn',
label: 'amy@wong.cn',
image: 'image-link',
name: 'Amy Wong',
},
]}
/>

Searchable

Set searchable prop to enable search in select and nothingFound prop to provide label that will be shown when no options were found:

import { Select } from '@mantine/core';
function Demo() {
return (
<Select
label="Your favorite framework/library"
placeholder="Pick one"
searchable
nothingFound="No options"
data={['React', 'Angular', 'Svelte', 'Vue']}
/>
);
}

Clearable

Set clearable prop to enable clearing select value. When prop is true and value is selected clear button will replace chevron in right section:

import { Select } from '@mantine/core';
function Demo() {
return (
<>
<Select clearable />
<Select searchable clearable />
</>
);
}

Allow deselect items

To allow items deselection set allowDeselect, user will also be able to deselect item if clearable prop is set:

import { Select } from '@mantine/core';
function Demo() {
return <Select allowDeselect />;
}

Creatable

Set creatable and getCreateLabel props to enable creating new select item. Note that you will need to handle data state to manage item creation correctly:

import { useState } from 'react';
import { Select } from '@mantine/core';
export function Demo() {
const [data, setData] = useState(['React', 'Angular', 'Svelte', 'Vue']);
return (
<Select
label="Creatable Select"
data={data}
placeholder="Select items"
nothingFound="Nothing found"
searchable
creatable
getCreateLabel={(query) => `+ Create ${query}`}
onCreate={(query) => setData((current) => [...current, query])}
/>
);
}

Grouping items

import { Select } from '@mantine/core';
function Demo() {
return (
<Select
label="Your favorite Rick and Morty character"
placeholder="Pick one"
data={[
{ value: 'rick', label: 'Rick', group: 'Used to be a pickle' },
{ value: 'morty', label: 'Morty', group: 'Never was a pickle' },
{ value: 'beth', label: 'Beth', group: 'Never was a pickle' },
{ value: 'summer', label: 'Summer', group: 'Never was a pickle' },
]}
/>
);
}

Disable items

import { Select } from '@mantine/core';
function Demo() {
return (
<Select
label="Select with disabled items"
placeholder="Select something"
data={[
{ value: 'react', label: 'React', disabled: true },
{ value: 'ng', label: 'Angular', disabled: true },
{ value: 'svelte', label: 'Svelte' },
{ value: 'vue', label: 'Vue' },
]}
/>
);
}

Large data set

When dropdown height is exceeded dropdown becomes scrollable, to change max-height set maxDropdownHeight prop with value in px:

import { Select } from '@mantine/core';
const data = Array(50).fill(0).map((_, index) => `Item ${index}`);
function Demo() {
return (
<Select
label="What item is the best?"
placeholder="Pick one"
searchable
nothingFound="No options"
maxDropdownHeight={280}
data={data}
/>
);
}

Custom item component

You can change select item component and filtering logic that is used in search. To do so you will need to:

  • Add extra props to data objects
  • Create filter function which determines whether item should be added to the search results
  • Provide itemComponent which will consume data objects
import { forwardRef } from 'react';
import { Group, Avatar, Text, Select } from '@mantine/core';
const data = [
{
image: 'https://img.icons8.com/clouds/256/000000/futurama-bender.png',
label: 'Bender Bending Rodríguez',
value: 'Bender Bending Rodríguez',
description: 'Fascinated with cooking',
},
{
image: 'https://img.icons8.com/clouds/256/000000/futurama-mom.png',
label: 'Carol Miller',
value: 'Carol Miller',
description: 'One of the richest people on Earth',
},
{
image: 'https://img.icons8.com/clouds/256/000000/homer-simpson.png',
label: 'Homer Simpson',
value: 'Homer Simpson',
description: 'Overweight, lazy, and often ignorant',
},
{
image: 'https://img.icons8.com/clouds/256/000000/spongebob-squarepants.png',
label: 'Spongebob Squarepants',
value: 'Spongebob Squarepants',
description: 'Not just a sponge',
},
];
interface ItemProps extends React.ComponentPropsWithoutRef<'div'> {
image: string;
label: string;
description: string;
}
const SelectItem = forwardRef<HTMLDivElement, ItemProps>(
({ image, label, description, ...others }: ItemProps, ref) => (
<div ref={ref} {...others}>
<Group noWrap>
<Avatar src={image} />
<div>
<Text size="sm">{label}</Text>
<Text size="xs" color="dimmed">
{description}
</Text>
</div>
</Group>
</div>
)
);
function Demo() {
return (
<Select
label="Choose employee of the month"
placeholder="Pick one"
itemComponent={SelectItem}
data={data}
searchable
maxDropdownHeight={400}
nothingFound="Nobody here"
filter={(value, item) =>
item.label.toLowerCase().includes(value.toLowerCase().trim()) ||
item.description.toLowerCase().includes(value.toLowerCase().trim())
}
/>
);
}

Dropdown position

By default, dropdown is placed below the input and when there is not enough space, it flips to be above the input. To change this behavior, set dropdownPosition prop:

DropdownPosition
import { Select } from '@mantine/core';
function Demo() {
return <Select />;
}

Animations

By default, dropdown animations are turned off to increase responsiveness. You can enable them by setting optional props:

  • transition – premade transition name or custom transition styles object, see Transition component for all available options
  • transitionDuration – transition duration in ms, defaults to 0
  • transitionTimingFunction – defaults to theme.transitionTimingFunction
import { Select } from '@mantine/core';
function Demo() {
return (
<Select
transition="pop-top-left"
transitionDuration={80}
transitionTimingFunction="ease"
/>
);
}

Native scrollbars

By default, Select uses ScrollArea to render dropdown. If you want to use native scrollbars instead, set div as a dropdown component:

import { Select } from '@mantine/core';
const data = Array(50).fill(0).map((_, index) => `Item ${index}`);
function Demo() {
return (
<Select
data={data}
label="Select with native scrollbars"
placeholder="Dropdown with native scrollbars"
dropdownComponent="div"
/>
);
}

With icon

import { Select } from '@mantine/core';
import { Hash } from 'tabler-icons-react';
function Demo() {
return (
<Select
label="Pick a hashtag"
placeholder="Pick a hashtag"
data={['React', 'Angular', 'Svelte', 'Vue']}
icon={<Hash size={14} />}
/>
);
}

Invalid state and error

Pick at least one item
// Error as boolean – red border color
<Select error />
// Error as React node – red border color and message below input
<Select error="Pick at least one item" />

Disabled state

import { Select } from '@mantine/core';
function Demo() {
return <Select disabled />;
}

Right section

You can replace icon in right section with rightSection prop. Note that in this case clearable option will not work and will need to handle it yourself:

import { Select } from '@mantine/core';
import { ChevronDown } from 'tabler-icons-react';
function Demo() {
return (
<Select
label="Your favorite framework/library"
placeholder="Pick one"
rightSection={<ChevronDown size={14} />}
rightSectionWidth={30}
styles={{ rightSection: { pointerEvents: 'none' } }}
data={['React', 'Angular', 'Svelte', 'Vue']}
/>
);
}

Input props

Radius
xs
sm
md
lg
xl
Size
xs
sm
md
lg
xl
import { Select } from '@mantine/core';
function Demo() {
return (
<Select
data={['React', 'Angular', 'Svelte', 'Vue']}
placeholder="Pick one"
label="Your favorite framework/library"
required
/>
);
}

Get input element ref

import { useRef } from 'react';
import { Select } from '@mantine/core';
function Demo() {
const ref = useRef<HTMLInputElement>();
return <Select ref={ref} data={[]} />;
}

Accessibility

Provide aria-label in case you use component without label for screen reader support:

<Select /> // -> not ok, select is not labeled
<Select label="My select" /> // -> ok, select and label is connected
<Select aria-label="My select" /> // -> ok, label is not visible but will be announced by screen reader

If you use clearable option set clearButtonLabel:

<Select clearable clearButtonLabel="Clear select field" />