Searchable Select
A Select component with Autocomplete filtering.
Example#
import type {ListBoxItemProps} from 'react-aria-components';
import {Autocomplete, Button, Input, Label, ListBox, ListBoxItem, Popover, SearchField, Select, SelectValue, useFilter} from 'react-aria-components';
import {CheckIcon, CheckIcon, ChevronsUpDownIcon, SearchIcon, XIcon} from 'lucide-react';
function SelectExample() {
let { contains } = useFilter({ sensitivity: 'base' });
return (
<div className="bg-linear-to-br from-cyan-200 to-blue-400 p-8 sm:h-[350px] rounded-lg flex justify-center">
<Select className="flex flex-col gap-1 w-[200px]">
<Label className="text-black cursor-default">Language</Label>
<Button className="flex items-center cursor-default rounded-lg border-0 bg-white/90 pressed:bg-white transition py-2 pl-5 pr-2 text-base text-left leading-normal ring-1 ring-black/5 shadow-md text-gray-700 focus:outline-hidden focus-visible:outline-2 outline-black outline-offset-3 focus-visible:ring-black/25">
<SelectValue className="flex-1 truncate" />
<ChevronsUpDownIcon className="w-4 h-4" />
</Button>
<Popover className="!max-h-80 w-(--trigger-width) flex flex-col rounded-md bg-white text-base shadow-lg ring-1 ring-black/5 entering:animate-in entering:fade-in exiting:animate-out exiting:fade-out">
<Autocomplete filter={contains}>
<SearchField
aria-label="Search"
autoFocus
className="group flex items-center bg-white forced-colors:bg-[Field] border-2 border-gray-300 has-focus:border-sky-600 rounded-full m-1"
>
<SearchIcon
aria-hidden
className="w-4 h-4 ml-2 text-gray-600 forced-colors:text-[ButtonText]"
/>
<Input
placeholder="Search languages"
className="px-2 py-1 flex-1 min-w-0 border-none outline outline-0 bg-white text-base text-gray-800 placeholder-gray-500 font-[inherit] [&::-webkit-search-cancel-button]:hidden"
/>
<Button className="text-sm text-center transition rounded-full border-0 p-1 flex items-center justify-center text-gray-600 bg-transparent hover:bg-black/[5%] pressed:bg-black/10 mr-1 w-6 group-empty:invisible">
<XIcon aria-hidden className="w-4 h-4" />
</Button>
</SearchField>
<ListBox
items={languages}
className="outline-hidden p-1 overflow-auto flex-1 scroll-pb-1"
>
{(item) => <SelectItem>{item.name}</SelectItem>}
</ListBox>
</Autocomplete>
</Popover>
</Select>
</div>
);
}
function SelectItem(props: ListBoxItemProps & { children: string }) {
return (
<ListBoxItem
{...props}
textValue={props.children}
className="group flex items-center gap-2 cursor-default select-none py-2 px-4 outline-hidden rounded-sm text-gray-900 focus:bg-sky-600 focus:text-white"
>
{({ isSelected }) => (
<>
<span className="flex-1 flex items-center gap-2 truncate font-normal group-selected:font-medium">
{props.children}
</span>
<span className="w-5 flex items-center text-sky-600 group-focus:text-white">
{isSelected && <CheckIcon size="S" />}
</span>
</>
)}
</ListBoxItem>
);
}
import type {ListBoxItemProps} from 'react-aria-components';
import {
Autocomplete,
Button,
Input,
Label,
ListBox,
ListBoxItem,
Popover,
SearchField,
Select,
SelectValue,
useFilter
} from 'react-aria-components';
import {
CheckIcon,
CheckIcon,
ChevronsUpDownIcon,
SearchIcon,
XIcon
} from 'lucide-react';
function SelectExample() {
let { contains } = useFilter({ sensitivity: 'base' });
return (
<div className="bg-linear-to-br from-cyan-200 to-blue-400 p-8 sm:h-[350px] rounded-lg flex justify-center">
<Select className="flex flex-col gap-1 w-[200px]">
<Label className="text-black cursor-default">
Language
</Label>
<Button className="flex items-center cursor-default rounded-lg border-0 bg-white/90 pressed:bg-white transition py-2 pl-5 pr-2 text-base text-left leading-normal ring-1 ring-black/5 shadow-md text-gray-700 focus:outline-hidden focus-visible:outline-2 outline-black outline-offset-3 focus-visible:ring-black/25">
<SelectValue className="flex-1 truncate" />
<ChevronsUpDownIcon className="w-4 h-4" />
</Button>
<Popover className="!max-h-80 w-(--trigger-width) flex flex-col rounded-md bg-white text-base shadow-lg ring-1 ring-black/5 entering:animate-in entering:fade-in exiting:animate-out exiting:fade-out">
<Autocomplete filter={contains}>
<SearchField
aria-label="Search"
autoFocus
className="group flex items-center bg-white forced-colors:bg-[Field] border-2 border-gray-300 has-focus:border-sky-600 rounded-full m-1"
>
<SearchIcon
aria-hidden
className="w-4 h-4 ml-2 text-gray-600 forced-colors:text-[ButtonText]"
/>
<Input
placeholder="Search languages"
className="px-2 py-1 flex-1 min-w-0 border-none outline outline-0 bg-white text-base text-gray-800 placeholder-gray-500 font-[inherit] [&::-webkit-search-cancel-button]:hidden"
/>
<Button className="text-sm text-center transition rounded-full border-0 p-1 flex items-center justify-center text-gray-600 bg-transparent hover:bg-black/[5%] pressed:bg-black/10 mr-1 w-6 group-empty:invisible">
<XIcon aria-hidden className="w-4 h-4" />
</Button>
</SearchField>
<ListBox
items={languages}
className="outline-hidden p-1 overflow-auto flex-1 scroll-pb-1"
>
{(item) => (
<SelectItem>{item.name}</SelectItem>
)}
</ListBox>
</Autocomplete>
</Popover>
</Select>
</div>
);
}
function SelectItem(
props: ListBoxItemProps & { children: string }
) {
return (
<ListBoxItem
{...props}
textValue={props.children}
className="group flex items-center gap-2 cursor-default select-none py-2 px-4 outline-hidden rounded-sm text-gray-900 focus:bg-sky-600 focus:text-white"
>
{({ isSelected }) => (
<>
<span className="flex-1 flex items-center gap-2 truncate font-normal group-selected:font-medium">
{props.children}
</span>
<span className="w-5 flex items-center text-sky-600 group-focus:text-white">
{isSelected && <CheckIcon size="S" />}
</span>
</>
)}
</ListBoxItem>
);
}
import type {ListBoxItemProps} from 'react-aria-components';
import {
Autocomplete,
Button,
Input,
Label,
ListBox,
ListBoxItem,
Popover,
SearchField,
Select,
SelectValue,
useFilter
} from 'react-aria-components';
import {
CheckIcon,
CheckIcon,
ChevronsUpDownIcon,
SearchIcon,
XIcon
} from 'lucide-react';
function SelectExample() {
let { contains } =
useFilter({
sensitivity: 'base'
});
return (
<div className="bg-linear-to-br from-cyan-200 to-blue-400 p-8 sm:h-[350px] rounded-lg flex justify-center">
<Select className="flex flex-col gap-1 w-[200px]">
<Label className="text-black cursor-default">
Language
</Label>
<Button className="flex items-center cursor-default rounded-lg border-0 bg-white/90 pressed:bg-white transition py-2 pl-5 pr-2 text-base text-left leading-normal ring-1 ring-black/5 shadow-md text-gray-700 focus:outline-hidden focus-visible:outline-2 outline-black outline-offset-3 focus-visible:ring-black/25">
<SelectValue className="flex-1 truncate" />
<ChevronsUpDownIcon className="w-4 h-4" />
</Button>
<Popover className="!max-h-80 w-(--trigger-width) flex flex-col rounded-md bg-white text-base shadow-lg ring-1 ring-black/5 entering:animate-in entering:fade-in exiting:animate-out exiting:fade-out">
<Autocomplete
filter={contains}
>
<SearchField
aria-label="Search"
autoFocus
className="group flex items-center bg-white forced-colors:bg-[Field] border-2 border-gray-300 has-focus:border-sky-600 rounded-full m-1"
>
<SearchIcon
aria-hidden
className="w-4 h-4 ml-2 text-gray-600 forced-colors:text-[ButtonText]"
/>
<Input
placeholder="Search languages"
className="px-2 py-1 flex-1 min-w-0 border-none outline outline-0 bg-white text-base text-gray-800 placeholder-gray-500 font-[inherit] [&::-webkit-search-cancel-button]:hidden"
/>
<Button className="text-sm text-center transition rounded-full border-0 p-1 flex items-center justify-center text-gray-600 bg-transparent hover:bg-black/[5%] pressed:bg-black/10 mr-1 w-6 group-empty:invisible">
<XIcon
aria-hidden
className="w-4 h-4"
/>
</Button>
</SearchField>
<ListBox
items={languages}
className="outline-hidden p-1 overflow-auto flex-1 scroll-pb-1"
>
{(item) => (
<SelectItem>
{item
.name}
</SelectItem>
)}
</ListBox>
</Autocomplete>
</Popover>
</Select>
</div>
);
}
function SelectItem(
props:
& ListBoxItemProps
& {
children: string;
}
) {
return (
<ListBoxItem
{...props}
textValue={props
.children}
className="group flex items-center gap-2 cursor-default select-none py-2 px-4 outline-hidden rounded-sm text-gray-900 focus:bg-sky-600 focus:text-white"
>
{(
{ isSelected }
) => (
<>
<span className="flex-1 flex items-center gap-2 truncate font-normal group-selected:font-medium">
{props
.children}
</span>
<span className="w-5 flex items-center text-sky-600 group-focus:text-white">
{isSelected &&
(
<CheckIcon size="S" />
)}
</span>
</>
)}
</ListBoxItem>
);
}
Tailwind config#
This example uses the following plugins:
When using Tailwind v4, add them to your CSS:
@import "tailwindcss";
@plugin "tailwindcss-react-aria-components";
@plugin "tailwindcss-animate";
@import "tailwindcss";
@plugin "tailwindcss-react-aria-components";
@plugin "tailwindcss-animate";
@import "tailwindcss";
@plugin "tailwindcss-react-aria-components";
@plugin "tailwindcss-animate";
Tailwind v3
When using Tailwind v3, add the plugins to your tailwind.config.js
instead:
module.exports = {
// ...
plugins: [
require('tailwindcss-react-aria-components'),
require('tailwindcss-animate')
]
};
module.exports = {
// ...
plugins: [
require('tailwindcss-react-aria-components'),
require('tailwindcss-animate')
]
};
module.exports = {
// ...
plugins: [
require(
'tailwindcss-react-aria-components'
),
require(
'tailwindcss-animate'
)
]
};
Note: When using Tailwind v3, install tailwindcss-react-aria-components
version 1.x instead of 2.x.
Components#
Select
A select displays a collapsible list of options, and allows a user to select one of them.
ListBox
A listbox allows a user to select one or more options from a list.
Popover
A popover displays content in context with a trigger element.
Button
A button allows a user to perform an action.
SearchField
A search field allows a user to enter and clear a search query.