About
Here is Combobox component
Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Combobox to your project and fits it to your theme.
Code
How Combobox is used: the demo's code. The component's own source comes with a free account.
Usage.tsx
'use client';
import * as React from 'react';
import { Combobox } from '@base-ui-components/react/combobox';
import { useVirtualizer } from '@tanstack/react-virtual';
export default function ExampleVirtualizedCombobox() {
const [open, setOpen] = React.useState(false);
const [searchValue, setSearchValue] = React.useState('');
const [value, setValue] = React.useState<string | null>(null);
const scrollElementRef = React.useRef<HTMLDivElement | null>(null);
const { contains } = Combobox.useFilter({ sensitivity: 'base', value });
const filteredItems = React.useMemo(() => {
return virtualItems.filter((item) => contains(item, searchValue));
}, [contains, searchValue]);
const virtualizer = useVirtualizer({
enabled: open,
count: filteredItems.length,
getScrollElement: () => scrollElementRef.current,
estimateSize: () => 32,
overscan: 20,
paddingStart: 8,
paddingEnd: 8,
scrollPaddingEnd: 8,
scrollPaddingStart: 8,
});
const handleScrollElementRef = React.useCallback(
(element: HTMLDivElement) => {
scrollElementRef.current = element;
if (element) {
virtualizer.measure();
}
},
[virtualizer],
);
const totalSize = virtualizer.getTotalSize();
const totalSizePx = `${totalSize}px`;
return (
<Combobox.Root
virtualized
filter={contains}
items={virtualItems}
open={open}
onOpenChange={setOpen}
inputValue={searchValue}
onInputValueChange={setSearchValue}
value={value}
onValueChange={setValue}
onItemHighlighted={(item, { type, index }) => {
if (!item) {
return;
}
const isStart = index === 0;
const isEnd = index === filteredItems.length - 1;
const shouldScroll = type === 'none' || (type === 'keyboard' && (isStart || isEnd));
if (shouldScroll) {
queueMicrotask(() => {
virtualizer.scrollToIndex(index, { align: isEnd ? 'start' : 'end' });
});
}
}}
>
<label className="flex flex-col gap-1 text-sm leading-5 font-medium text-gray-900">
Search 10,000 items
<Combobox.Input className="h-10 w-64 rounded-md font-normal border border-gray-200 pl-3.5 text-base text-gray-900 bg-[canvas] focus:outline focus:outline-2 focus:-outline-offset-1 focus:outline-blue-800" />
</label>
<Combobox.Portal>
<Combobox.Positioner className="outline-none" sideOffset={4}>
<Combobox.Popup className="w-[var(--anchor-width)] max-h-[min(22rem,var(--available-height))] max-w-[var(--available-width)] rounded-md bg-[canvas] text-gray-900 outline-1 outline-gray-200 shadow-lg shadow-gray-200 dark:-outline-offset-1 dark:outline-gray-300">
<Combobox.Empty className="px-4 py-4 text-[0.925rem] leading-4 text-gray-600 empty:m-0 empty:p-0">
No items found.
</Combobox.Empty>
<Combobox.List className="p-0">
{filteredItems.length > 0 && (
<div
role="presentation"
ref={handleScrollElementRef}
className="h-[min(22rem,var(--total-size))] max-h-[var(--available-height)] overflow-auto overscroll-contain scroll-p-2"
style={{ '--total-size': totalSizePx } as React.CSSProperties}
>
<div
role="presentation"
className="relative w-full"
style={{ height: totalSizePx }}
>
{virtualizer.getVirtualItems().map((virtualItem) => {
const item = filteredItems[virtualItem.index];
if (!item) {
return null;
}
return (
<Combobox.Item
key={virtualItem.key}
index={virtualItem.index}
value={item}
className="grid cursor-default grid-cols-[0.75rem_1fr] items-center gap-2 py-2 pr-8 pl-4 text-base leading-4 outline-none select-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-gray-50 data-[highlighted]:before:absolute data-[highlighted]:before:inset-x-2 data-[highlighted]:before:inset-y-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:rounded-sm data-[highlighted]:before:bg-gray-900"
aria-setsize={filteredItems.length}
aria-posinset={virtualItem.index + 1}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: `${virtualItem.size}px`,
transform: `translateY(${virtualItem.start}px)`,
}}
>
<Combobox.ItemIndicator className="col-start-1">
<CheckIcon className="size-3" />
</Combobox.ItemIndicator>
<div className="col-start-2">{item}</div>
</Combobox.Item>
);
})}
</div>
</div>
)}
</Combobox.List>
</Combobox.Popup>
</Combobox.Positioner>
</Combobox.Portal>
</Combobox.Root>
);
}
function CheckIcon(props: React.ComponentProps<'svg'>) {
return (
<svg fill="currentcolor" width="10" height="10" viewBox="0 0 10 10" {...props}>
<path d="M9.1603 1.12218C9.50684 1.34873 9.60427 1.81354 9.37792 2.16038L5.13603 8.66012C5.01614 8.8438 4.82192 8.96576 4.60451 8.99384C4.3871 9.02194 4.1683 8.95335 4.00574 8.80615L1.24664 6.30769C0.939709 6.02975 0.916013 5.55541 1.19372 5.24822C1.47142 4.94102 1.94536 4.91731 2.2523 5.19524L4.36085 7.10461L8.12299 1.33999C8.34934 0.993152 8.81376 0.895638 9.1603 1.12218Z" />
</svg>
);
}
const virtualItems = Array.from({ length: 10000 }, (_, i) => {
const indexLabel = String(i + 1).padStart(4, '0');
return `Item ${indexLabel}`;
});








