Radix Select with Combobox
Components
Explore the Ariakit components used in this example:
Basic structure
<RadixSelect.Root>
<RadixSelect.Trigger />
<RadixSelect.Content>
<RadixSelect.Item asChild>
</RadixSelect.Item>
</RadixSelect.Content>
</RadixSelect.Root>
Keeping the Select open when the viewport resizes
Radix Select closes its popup when the window resizes. On mobile browsers, showing the on-screen keyboard may resize the window and immediately close the popup. Since this example uses Radix's position="popper" mode, its Floating UI positioning already reacts to viewport changes. We can keep this controlled Select open while the resize event is being handled and let the popup reposition instead:
function useRadixSelectOpenState() {
const [open, setOpen] = useState(false);
const isWindowResizingRef = useRef(false);
useEffect(() => {
let resetTimer = 0;
const onResize = () => {
isWindowResizingRef.current = true;
window.clearTimeout(resetTimer);
resetTimer = window.setTimeout(() => {
isWindowResizingRef.current = false;
}, 0);
};
window.addEventListener("resize", onResize, true);
return () => {
window.removeEventListener("resize", onResize, true);
window.clearTimeout(resetTimer);
};
}, []);
const onOpenChange = useCallback((nextOpen) => {
if (!nextOpen && isWindowResizingRef.current) return;
setOpen(nextOpen);
}, []);
return { open, setOpen, onOpenChange };
}
The capture listener marks the current resize before Radix handles it without stopping propagation, so Floating UI and other resize listeners still run. The timer clears the marker in the next task because browsers may run microtasks between event listeners.
This intentionally keeps the popup open for every window resize, including virtual keyboards, orientation changes, and ordinary resizing. Browsers don't expose a reliable way to distinguish those causes.
Filtering options
The Ariakit Combobox component doesn't dictate how you filter the items. It focuses solely on the ComboboxItem elements you render. Consequently, you can render items conditionally based on the value state.
We use the setValue callback in combination with React.startTransition to update our search value state without blocking the UI:
const [searchValue, setSearchValue] = useState("");
React.startTransition(() => {
setSearchValue(value);
});
}}
>
You're free to use any matching algorithm or library to filter the items. In this example, we use match-sorter:
const matches = useMemo(() => {
return matchSorter(languages, searchValue, {
keys: ["label", "value"],
});
}, [languages, searchValue]);
Rendering SelectItem as ComboboxItem
To get the items to function as both a Radix SelectItem and an Ariakit ComboboxItem, we have to combine the two components:
<RadixSelect.Item value="en" asChild>
<RadixSelect.ItemText>English</RadixSelect.ItemText>
</RadixSelect.Item>
More examples











