Select with Combobox and Tabs
Abstracting Select to work alongside Combobox and Tab components, presenting a searchable, tabbed dropdown.
Select
Select with Combobox
Select with Tab
Select with Combobox and Tab
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
import { matchSorter } from "match-sorter";import { useMemo, useState } from "react";import * as defaultData from "./data.ts";import { BranchIcon, TagIcon } from "./icons.tsx";import {SelectTab,SelectTabList,SelectTabPanel,} from "./select.tsx";import type { SelectProps } from "./select.tsx";import "./theme.css";export default function Example() {const [data, setData] = useState(defaultData);const [tab, setTab] = useState<SelectProps["tab"]>("branches");const [value, setValue] = useState("main");const [searchValue, setSearchValue] = useState("");const values = tab && tab in data ? data[tab as keyof typeof data] : null;const matches = useMemo(() => {if (!values) return [];if (!searchValue) return values;return matchSorter(values, searchValue);}, [values, searchValue]);const TabIcon = tab === "branches" ? BranchIcon : TagIcon;const placeholder =tab === "branches" ? "Find or create a branch..." : "Find a tag...";const canAddBranch =!!searchValue && !matches.includes(searchValue) && tab === "branches";const customItem = canAddBranch && (<>icon={<BranchIcon />}onClick={() => {setData((data) => ({...data,branches: [...data.branches, searchValue],}));}}>Create branch <strong>{searchValue}</strong> from{" "}<strong>{value}</strong></>);const empty = !matches.length && (<div className="py-6 text-center">No matches found</div>);return (<div className="flex max-w-full flex-wrap justify-center gap-2">label={<div hidden>Select</div>}icon={<TabIcon />}value={value}setValue={setValue}>{values?.map((value) => ())}label={<div hidden>Select with Combobox</div>}icon={<TabIcon />}combobox={<input placeholder={placeholder} />}value={value}setValue={setValue}onSearch={setSearchValue}>{matches?.map((value) => ())}{customItem || empty}label={<div hidden>Select with Tab</div>}icon={<TabIcon />}tab={tab}setTab={setTab}value={value}setValue={setValue}><SelectTabList><SelectTab id="branches">Branches</SelectTab><SelectTab id="tags">Tags</SelectTab></SelectTabList><SelectTabPanel>{values?.map((value) => ())}</SelectTabPanel>label={<div hidden>Select with Combobox and Tab</div>}icon={<TabIcon />}heading="Switch branches/tags"combobox={<input placeholder={placeholder} />}tab={tab}setTab={setTab}value={value}setValue={setValue}onSearch={setSearchValue}><SelectTabList><SelectTab id="branches">Branches</SelectTab><SelectTab id="tags">Tags</SelectTab></SelectTabList><SelectTabPanel>{matches.map((value) => ())}{customItem || empty}</SelectTabPanel></div>);}import { matchSorter } from "match-sorter";import { useMemo, useState } from "react";import * as defaultData from "./data.ts";import { BranchIcon, TagIcon } from "./icons.tsx";import {SelectTab,SelectTabList,SelectTabPanel,} from "./select.tsx";import type { SelectProps } from "./select.tsx";import "./theme.css";export default function Example() {const [data, setData] = useState(defaultData);const [tab, setTab] = useState<SelectProps["tab"]>("branches");const [value, setValue] = useState("main");const [searchValue, setSearchValue] = useState("");const values = tab && tab in data ? data[tab as keyof typeof data] : null;const matches = useMemo(() => {if (!values) return [];if (!searchValue) return values;return matchSorter(values, searchValue);}, [values, searchValue]);const TabIcon = tab === "branches" ? BranchIcon : TagIcon;const placeholder =tab === "branches" ? "Find or create a branch..." : "Find a tag...";const canAddBranch =!!searchValue && !matches.includes(searchValue) && tab === "branches";const customItem = canAddBranch && (<>icon={<BranchIcon />}onClick={() => {setData((data) => ({...data,branches: [...data.branches, searchValue],}));}}>Create branch <strong>{searchValue}</strong> from{" "}<strong>{value}</strong></>);const empty = !matches.length && (<div className="py-6 text-center">No matches found</div>);return (<div className="flex max-w-full flex-wrap justify-center gap-2">label={<div hidden>Select</div>}icon={<TabIcon />}value={value}setValue={setValue}>{values?.map((value) => ())}label={<div hidden>Select with Combobox</div>}icon={<TabIcon />}combobox={<input placeholder={placeholder} />}value={value}setValue={setValue}onSearch={setSearchValue}>{matches?.map((value) => ())}{customItem || empty}label={<div hidden>Select with Tab</div>}icon={<TabIcon />}tab={tab}setTab={setTab}value={value}setValue={setValue}><SelectTabList><SelectTab id="branches">Branches</SelectTab><SelectTab id="tags">Tags</SelectTab></SelectTabList><SelectTabPanel>{values?.map((value) => ())}</SelectTabPanel>label={<div hidden>Select with Combobox and Tab</div>}icon={<TabIcon />}heading="Switch branches/tags"combobox={<input placeholder={placeholder} />}tab={tab}setTab={setTab}value={value}setValue={setValue}onSearch={setSearchValue}><SelectTabList><SelectTab id="branches">Branches</SelectTab><SelectTab id="tags">Tags</SelectTab></SelectTabList><SelectTabPanel>{matches.map((value) => ())}{customItem || empty}</SelectTabPanel></div>);}
Related examples
Select with ComboboxCombining Select and Combobox to create a dropdown with a search field that can be used to filter items.
Animated SelectAnimating Select using CSS transitions in React. The component waits for the transition to finish before completely hiding the popover.
Combobox with TabsOrganizing Combobox with Tab components that support mouse, keyboard, and screen reader interactions. The UI remains responsive by using React.startTransition.
Form with SelectCombining Form and Select to create an accessible custom select widget that works with the browser's built-in validation and native form submission.
Toolbar with SelectRendering Select as a ToolbarItem inside a Toolbar.
Select with Next.js App RouterControlling the value of a Select component via the URL using the Next.js App Router and React.useOptimistic to ensure a responsive and accessible UI.