pnpm dlx dreamy add selectPass options as an items array on Select.Root. Each item requires value and label.
const fruits = [
{ value: "strawberry", label: "Strawberry" },
{ value: "banana", label: "Banana" },
{ value: "orange", label: "Orange" },
];
<Select.Root items={fruits}>
<Select.Trigger placeholder="Select a favorite fruit" />
<Select.Content />
</Select.Root>Use popoverProps on Select.Root to pass behavior props to the internal Popover . Keep placement options on the root positioning prop. The dropdown joins the shared Portal stack automatically; use popoverProps.portalProps.containerRef only for a native or third-party overlay root.
<Select.Root
items={fruits}
popoverProps={{ lazyBehavior: "unmount" }}
>
<Select.Trigger placeholder="Select a favorite fruit" />
<Select.Content />
</Select.Root>Use the renderItem prop on Select.Root to customise how each option is displayed. The render function receives the full item type from your items array, so extra fields such as icon are type-safe.
const fruits = [
{ value: "cherry", label: "Cherry", icon: <LuCherry /> },
{ value: "banana", label: "Banana", icon: <LuBanana /> },
{ value: "orange", label: "Orange", icon: <LuCitrus /> },
];
<Select.Root
items={fruits}
renderItem={(item) => (
<HStack gap={2}>
{item.icon}
<Text>{item.label}</Text>
</HStack>
)}
>
<Select.Trigger placeholder="Select a favorite fruit" />
<Select.Content />
</Select.Root>Select comes with 6 different sizes.
<Select.Root size="md" items={fruits}>
<Select.Trigger placeholder="Select a favorite fruit" />
<Select.Content />
</Select.Root>The trigger can be used in outline, solid, or filledOutline style.
<Select.Root triggerVariant="outline" items={fruits}>
<Select.Trigger placeholder="Select a favorite fruit" />
<Select.Content />
</Select.Root>
<Select.Root triggerVariant="solid" items={fruits}>
<Select.Trigger placeholder="Select a favorite fruit" />
<Select.Content />
</Select.Root>
<Select.Root triggerVariant="filledOutline" items={fruits}>
<Select.Trigger placeholder="Select a favorite fruit" />
<Select.Content />
</Select.Root>The dropdown list can be plain (padded, rounded items) or stretched (edge-to-edge items).
<Select.Root variant="plain" items={fruits}>
<Select.Trigger placeholder="Select a favorite fruit" />
<Select.Content />
</Select.Root>You can customize the background color of the selected item. This will only apply if selectedStrategy is set to both or background.
primary
success
warning
info
error
none
export function ControlledSelect() {
const [value, setValue] = useState<string>("strawberry");
const fruits = [
{ value: "strawberry", label: "Strawberry" },
{ value: "banana", label: "Banana" },
{ value: "orange", label: "Orange" },
];
return (
<Select.Root
items={fruits}
value={value}
onChangeValue={setValue}
width="xs"
>
<Select.Trigger placeholder="Select a favorite fruit" />
<Select.Content />
</Select.Root>
);
}You can customize how the selected value is marked as selected.
both
checkmark
background
Single icon is useful if you want to have a global icon for the trigger, instead of having it on each item.
<Select.Root items={fruits}>
<Select.Trigger placeholder="Select a favorite fruit" icon={<LuCherry />} />
<Select.Content />
</Select.Root>Pass isClearable prop to enable clear button.
You can use the isMultiple prop to allow multiple selections. onChangeValue returns an array of values. By default, the trigger shows selected labels joined with commas (truncated when they overflow).
<Select.Root isMultiple items={fruits}>
<Select.Trigger placeholder="Select a favorite fruit" />
<Select.Content />
</Select.Root>
// Trigger shows: "Strawberry, Banana" (truncated with ellipsis when too long)Use multipleSelectedText on Select.Trigger to override the default comma-separated labels.
<Select.Trigger
placeholder="Select a favorite fruit"
multipleSelectedText={(selected) => `${selected.length} selected`}
/>You can fetch data when the Select is opened and update the items array. Use showItems={false} on Select.Content to render custom UI (such as a loading spinner) instead of the default items list.
export function AsyncSelect() {
const [isLoading, setIsLoading] = useState(false);
const [items, setItems] = useState<{ value: string; label: string }[]>([]);
function fetchFruits() {
if (items.length > 0) return;
setIsLoading(true);
fetch("/api/fake-select-data")
.then((res) => res.json())
.then((data: string[]) =>
setItems(data.map((fruit) => ({ value: fruit, label: fruit })))
)
.finally(() => setIsLoading(false));
}
return (
<Select.Root items={items} onOpen={fetchFruits} width="xs">
<Select.Trigger placeholder="Select a favorite fruit" />
<Select.Content showItems={!isLoading}>
<Spinner color="primary" py={4} />
</Select.Content>
</Select.Root>
);
}For better performance with large lists (100+ items), use Select.VirtualContent instead of Select.Content. It only renders visible items.
const manyItems = Array.from({ length: 250 }, (_, index) => ({
value: index.toString(),
label: `Item ${index + 1}`,
}));
<Select.Root items={manyItems}>
<Select.Trigger placeholder="Select a number" />
<Select.VirtualContent />
</Select.Root>Props
estimatedItemHeight- Estimated height of each item in pixels. For different select sizes:xs:26,sm:28,md:32,lg:40. Default:32overscan- Number of items to render outside the visible area. Default:5maxHeight- Maximum height of the virtualized list container in pixels. Default:300
No extra prop is needed inside Dreamy UI overlays. Keep the Select portal enabled so its dropdown scope (1000) nests under the parent scope and escapes clipped or scrolling content. In a Modal, it resolves to calc(1400 + 1000) automatically.
Later sibling Modals still cover earlier Modal subtrees, and same-layer dropdowns follow open order. Do not disable the portal or add a manual layer or z-index. For a native or third-party overlay, pass its non-scrolling root with popoverProps={{ portalProps: { containerRef } }}.
const fruits = [
{ value: "cherry", label: "Cherry" },
{ value: "banana", label: "Banana" },
{ value: "orange", label: "Orange" },
];
export function SelectInModal() {
const { isOpen, onOpen, onClose } = useControllable();
return (
<>
<Button onClick={onOpen} variant="primary">
Open modal
</Button>
<Modal.Root isOpen={isOpen} onClose={onClose}>
<Modal.Overlay />
<Modal.Content>
<Modal.Header>Select in a modal</Modal.Header>
<Modal.CloseButton />
<Modal.Body>
<Select.Root
defaultValue="cherry"
items={fruits}
width="xs"
>
<Select.Trigger placeholder="Select a favorite fruit" />
<Select.Content />
</Select.Root>
</Modal.Body>
<Modal.Footer>
<Button onClick={onClose}>Close</Button>
</Modal.Footer>
</Modal.Content>
</Modal.Root>
</>
);
}You can customize whether the Select should close when an item is selected. Default is true for non-multiple select, false for multiple select.
You can disable the animation of the Select by setting the reduceMotion prop to true.