Buttons
Data Display
Layout
Navigation
Select
Pick one or more values from a list in a dropdown.
React, Vue, Solid, and Svelte
import { Portal } from "@ark-ui/react/portal"
import { createListCollection, Select } from "@isbatak/react-ui/select"
const frameworks = createListCollection({ items: ["React", "Vue", "Solid", "Svelte", "Angular"] })
export function SelectDemo() {
return (
<Select.Root collection={frameworks} maxW="xs">
<Select.HiddenSelect />
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.Item item="React">
<Select.ItemText>React</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Vue">
<Select.ItemText>Vue</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Solid">
<Select.ItemText>Solid</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Svelte">
<Select.ItemText>Svelte</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Angular">
<Select.ItemText>Angular</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}Built on Ark UI's Select. The items come from a collection made with createListCollection, or useListCollection when
they change. The trigger looks like an input: a border that darkens on hover and takes the focus color while the list is
open.
Usage
import { Portal } from "@ark-ui/react/portal"
import { createListCollection, Select } from "@isbatak/react-ui/select"
const frameworks = createListCollection({ items: ["React", "Vue", "Solid", "Svelte", "Angular"] })
export function SelectDemo() {
return (
<Select.Root collection={frameworks} maxW="xs">
<Select.HiddenSelect />
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.Item item="React">
<Select.ItemText>React</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Vue">
<Select.ItemText>Vue</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Solid">
<Select.ItemText>Solid</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Svelte">
<Select.ItemText>Svelte</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Angular">
<Select.ItemText>Angular</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}<Select.Root collection={collection}>
<Select.HiddenSelect />
<Select.Label />
<Select.Control>
<Select.Trigger>
<Select.ValueText />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.ClearTrigger />
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel />
<Select.Item item={item}>
<Select.ItemText />
<Select.ItemIndicator />
</Select.Item>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>Select.Indicator, Select.ItemIndicator and Select.ClearTrigger render a chevron, a check and a cross when they
have no children. Select.HiddenSelect renders a native <select> so the value is sent with a form.
Examples
Sizes
Set size to xs, sm, md or lg. The default is md.
import { Portal } from "@ark-ui/react/portal"
import { createListCollection, Select } from "@isbatak/react-ui/select"
import { Stack } from "styled-system/jsx"
const frameworks = createListCollection({ items: ["React", "Vue", "Solid", "Svelte", "Angular"] })
export function SelectSizesDemo() {
return (
<Stack gap="5" w="full" maxW="xs">
<Select.Root collection={frameworks} size="xs">
<Select.HiddenSelect />
<Select.Label>size xs</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.Item item="React">
<Select.ItemText>React</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Vue">
<Select.ItemText>Vue</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Solid">
<Select.ItemText>Solid</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Svelte">
<Select.ItemText>Svelte</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Angular">
<Select.ItemText>Angular</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
<Select.Root collection={frameworks} size="sm">
<Select.HiddenSelect />
<Select.Label>size sm</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.Item item="React">
<Select.ItemText>React</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Vue">
<Select.ItemText>Vue</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Solid">
<Select.ItemText>Solid</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Svelte">
<Select.ItemText>Svelte</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Angular">
<Select.ItemText>Angular</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
<Select.Root collection={frameworks} size="md">
<Select.HiddenSelect />
<Select.Label>size md</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.Item item="React">
<Select.ItemText>React</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Vue">
<Select.ItemText>Vue</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Solid">
<Select.ItemText>Solid</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Svelte">
<Select.ItemText>Svelte</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Angular">
<Select.ItemText>Angular</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
<Select.Root collection={frameworks} size="lg">
<Select.HiddenSelect />
<Select.Label>size lg</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.Item item="React">
<Select.ItemText>React</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Vue">
<Select.ItemText>Vue</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Solid">
<Select.ItemText>Solid</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Svelte">
<Select.ItemText>Svelte</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Angular">
<Select.ItemText>Angular</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
</Stack>
)
}Variants
Set variant to outline, subtle or ghost. The default is outline.
import { Portal } from "@ark-ui/react/portal"
import { createListCollection, Select } from "@isbatak/react-ui/select"
import { Stack } from "styled-system/jsx"
const frameworks = createListCollection({ items: ["React", "Vue", "Solid", "Svelte", "Angular"] })
export function SelectVariantsDemo() {
return (
<Stack gap="5" w="full" maxW="xs">
<Select.Root collection={frameworks} variant="outline">
<Select.HiddenSelect />
<Select.Label>variant outline</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.Item item="React">
<Select.ItemText>React</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Vue">
<Select.ItemText>Vue</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Solid">
<Select.ItemText>Solid</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Svelte">
<Select.ItemText>Svelte</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Angular">
<Select.ItemText>Angular</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
<Select.Root collection={frameworks} variant="subtle">
<Select.HiddenSelect />
<Select.Label>variant subtle</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.Item item="React">
<Select.ItemText>React</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Vue">
<Select.ItemText>Vue</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Solid">
<Select.ItemText>Solid</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Svelte">
<Select.ItemText>Svelte</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Angular">
<Select.ItemText>Angular</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
<Select.Root collection={frameworks} variant="ghost">
<Select.HiddenSelect />
<Select.Label>variant ghost</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.Item item="React">
<Select.ItemText>React</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Vue">
<Select.ItemText>Vue</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Solid">
<Select.ItemText>Solid</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Svelte">
<Select.ItemText>Svelte</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Angular">
<Select.ItemText>Angular</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
</Stack>
)
}Option group
Pass groupBy to the collection and render each group with Select.ItemGroup and Select.ItemGroupLabel.
import { Portal } from "@ark-ui/react/portal"
import { createListCollection, Select } from "@isbatak/react-ui/select"
const frameworks = createListCollection({
items: [
{ label: "React", value: "react", group: "Frontend" },
{ label: "Vue", value: "vue", group: "Frontend" },
{ label: "Solid", value: "solid", group: "Frontend" },
{ label: "Django", value: "django", group: "Backend" },
{ label: "Rails", value: "rails", group: "Backend" },
{ label: "Laravel", value: "laravel", group: "Backend" },
],
groupBy: (item) => item.group,
})
export function SelectOptionGroupDemo() {
return (
<Select.Root collection={frameworks} maxW="xs">
<Select.HiddenSelect />
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frontend</Select.ItemGroupLabel>
<Select.Item item={{ label: "React", value: "react", group: "Frontend" }}>
<Select.ItemText>React</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item={{ label: "Vue", value: "vue", group: "Frontend" }}>
<Select.ItemText>Vue</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item={{ label: "Solid", value: "solid", group: "Frontend" }}>
<Select.ItemText>Solid</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.ItemGroup>
<Select.ItemGroup>
<Select.ItemGroupLabel>Backend</Select.ItemGroupLabel>
<Select.Item item={{ label: "Django", value: "django", group: "Backend" }}>
<Select.ItemText>Django</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item={{ label: "Rails", value: "rails", group: "Backend" }}>
<Select.ItemText>Rails</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item={{ label: "Laravel", value: "laravel", group: "Backend" }}>
<Select.ItemText>Laravel</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}Controlled
Pass value and onValueChange to own the selected values. The value is always an array, even for a single select.
Selected: Vue
import { Portal } from "@ark-ui/react/portal"
import { createListCollection, Select } from "@isbatak/react-ui/select"
import { useState } from "react"
import { Stack, styled } from "styled-system/jsx"
const frameworks = createListCollection({ items: ["React", "Vue", "Solid", "Svelte", "Angular"] })
export function SelectControlledDemo() {
const [value, setValue] = useState<string[]>(["Vue"])
return (
<Stack gap="3" w="full" maxW="xs">
<Select.Root collection={frameworks} value={value} onValueChange={(details) => setValue(details.value)}>
<Select.HiddenSelect />
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.Item item="React">
<Select.ItemText>React</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Vue">
<Select.ItemText>Vue</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Solid">
<Select.ItemText>Solid</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Svelte">
<Select.ItemText>Svelte</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Angular">
<Select.ItemText>Angular</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
<styled.p textStyle="sm" color="fg.muted">
Selected: {value.join(", ") || "nothing"}
</styled.p>
</Stack>
)
}Disabled
Set disabled on the root to disable the whole select. To disable single items, pass isItemDisabled to the
collection.
import { Portal } from "@ark-ui/react/portal"
import { createListCollection, Select } from "@isbatak/react-ui/select"
import { Stack } from "styled-system/jsx"
const frameworks = createListCollection({ items: ["React", "Vue", "Solid", "Svelte", "Angular"] })
const frameworksWithDisabled = createListCollection({
items: ["React", "Vue", "Solid", "Svelte", "Angular"],
isItemDisabled: (item) => item === "Angular",
})
export function SelectDisabledDemo() {
return (
<Stack gap="5" w="full" maxW="xs">
<Select.Root collection={frameworks} disabled>
<Select.HiddenSelect />
<Select.Label>Disabled select</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.Item item="React">
<Select.ItemText>React</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Vue">
<Select.ItemText>Vue</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Solid">
<Select.ItemText>Solid</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Svelte">
<Select.ItemText>Svelte</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Angular">
<Select.ItemText>Angular</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
<Select.Root collection={frameworksWithDisabled}>
<Select.HiddenSelect />
<Select.Label>Disabled item</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.Item item="React">
<Select.ItemText>React</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Vue">
<Select.ItemText>Vue</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Solid">
<Select.ItemText>Solid</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Svelte">
<Select.ItemText>Svelte</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Angular">
<Select.ItemText>Angular</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
</Stack>
)
}Invalid
Set invalid to give the trigger the error border. Add the error text below it, here with the field recipe's
errorText.
import { Portal } from "@ark-ui/react/portal"
import { createListCollection, Select } from "@isbatak/react-ui/select"
import { field } from "@isbatak/panda-ds/recipes"
const frameworks = createListCollection({ items: ["React", "Vue", "Solid", "Svelte", "Angular"] })
export function SelectInvalidDemo() {
const fieldClasses = field()
return (
<Select.Root collection={frameworks} invalid maxW="xs">
<Select.HiddenSelect />
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.Item item="React">
<Select.ItemText>React</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Vue">
<Select.ItemText>Vue</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Solid">
<Select.ItemText>Solid</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Svelte">
<Select.ItemText>Svelte</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Angular">
<Select.ItemText>Angular</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Portal>
<span className={fieldClasses.errorText}>Pick the framework your project uses.</span>
</Select.Root>
)
}Multiple
Set multiple to let people pick several values. The list stays open after each pick, and the value text lists them
all.
import { Portal } from "@ark-ui/react/portal"
import { createListCollection, Select } from "@isbatak/react-ui/select"
const frameworks = createListCollection({ items: ["React", "Vue", "Solid", "Svelte", "Angular"] })
export function SelectMultipleDemo() {
return (
<Select.Root collection={frameworks} multiple maxW="xs">
<Select.HiddenSelect />
<Select.Label>Frameworks</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select frameworks" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.Item item="React">
<Select.ItemText>React</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Vue">
<Select.ItemText>Vue</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Solid">
<Select.ItemText>Solid</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Svelte">
<Select.ItemText>Svelte</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Angular">
<Select.ItemText>Angular</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}Align item with trigger
The list opens below the trigger by default. Set alignItemWithTrigger to open it over the trigger instead, with the
selected item's text where the trigger shows the value, like a native select on macOS. It only does this for mouse and
keyboard input, and falls back to opening below the trigger on touch or when there is not enough room.
import { Portal } from "@ark-ui/react/portal"
import { createListCollection, Select } from "@isbatak/react-ui/select"
const frameworks = createListCollection({ items: ["React", "Vue", "Solid", "Svelte", "Angular"] })
export function SelectAlignItemWithTriggerDemo() {
return (
<Select.Root collection={frameworks} defaultValue={["Svelte"]} alignItemWithTrigger maxW="xs">
<Select.HiddenSelect />
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.Item item="React">
<Select.ItemText>React</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Vue">
<Select.ItemText>Vue</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Solid">
<Select.ItemText>Solid</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Svelte">
<Select.ItemText>Svelte</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Angular">
<Select.ItemText>Angular</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}Anchor tracking
The list follows the trigger when it moves or resizes. Set disableAnchorTracking to stop following layout shifts of
the trigger, so the list only moves when the page scrolls or the window resizes.
Positioning
Pass positioning to change where the list opens. An aligned list ignores the placement, so leave
alignItemWithTrigger off. The root sets sameWidth: true, so the list matches the trigger width unless you turn it
off.
import { Portal } from "@ark-ui/react/portal"
import { createListCollection, Select } from "@isbatak/react-ui/select"
const frameworks = createListCollection({ items: ["React", "Vue", "Solid", "Svelte", "Angular"] })
export function SelectPositioningDemo() {
return (
<Select.Root
collection={frameworks}
positioning={{ placement: "top", flip: false, sameWidth: false }}
maxW="xs"
>
<Select.HiddenSelect />
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.Item item="React">
<Select.ItemText>React</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Vue">
<Select.ItemText>Vue</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Solid">
<Select.ItemText>Solid</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Svelte">
<Select.ItemText>Svelte</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Angular">
<Select.ItemText>Angular</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}Clear trigger
Add Select.ClearTrigger to the indicator group to let people clear the value. It only shows when there is one.
import { Portal } from "@ark-ui/react/portal"
import { createListCollection, Select } from "@isbatak/react-ui/select"
const frameworks = createListCollection({ items: ["React", "Vue", "Solid", "Svelte", "Angular"] })
export function SelectClearTriggerDemo() {
return (
<Select.Root collection={frameworks} defaultValue={["React"]} maxW="xs">
<Select.HiddenSelect />
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.ClearTrigger />
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.Item item="React">
<Select.ItemText>React</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Vue">
<Select.ItemText>Vue</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Solid">
<Select.ItemText>Solid</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Svelte">
<Select.ItemText>Svelte</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Angular">
<Select.ItemText>Angular</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}Overflow
Long lists scroll inside the content. Set maxH on Select.Content to change the height, 24rem by default.
import { Portal } from "@ark-ui/react/portal"
import { createListCollection, Select } from "@isbatak/react-ui/select"
const countries = createListCollection({
items: [
"Afghanistan",
"Albania",
"Algeria",
"Andorra",
"Angola",
"Argentina",
"Armenia",
"Australia",
"Austria",
"Azerbaijan",
"Bahamas",
"Bahrain",
"Bangladesh",
"Belgium",
"Bolivia",
"Brazil",
"Bulgaria",
"Canada",
"Chile",
"Croatia",
],
})
export function SelectOverflowDemo() {
return (
<Select.Root collection={countries} maxW="xs">
<Select.HiddenSelect />
<Select.Label>Country</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select country" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content maxH="56">
<Select.Item item="Afghanistan">
<Select.ItemText>Afghanistan</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Albania">
<Select.ItemText>Albania</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Algeria">
<Select.ItemText>Algeria</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Andorra">
<Select.ItemText>Andorra</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Angola">
<Select.ItemText>Angola</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Argentina">
<Select.ItemText>Argentina</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Armenia">
<Select.ItemText>Armenia</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Australia">
<Select.ItemText>Australia</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Austria">
<Select.ItemText>Austria</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Azerbaijan">
<Select.ItemText>Azerbaijan</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Bahamas">
<Select.ItemText>Bahamas</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Bahrain">
<Select.ItemText>Bahrain</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Bangladesh">
<Select.ItemText>Bangladesh</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Belgium">
<Select.ItemText>Belgium</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Bolivia">
<Select.ItemText>Bolivia</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Brazil">
<Select.ItemText>Brazil</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Bulgaria">
<Select.ItemText>Bulgaria</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Canada">
<Select.ItemText>Canada</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Chile">
<Select.ItemText>Chile</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Croatia">
<Select.ItemText>Croatia</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}Item description
Put more than the text in an item, like a second line. Select.ItemText still sets the label the trigger shows.
import { Portal } from "@ark-ui/react/portal"
import { createListCollection, Select } from "@isbatak/react-ui/select"
import { Stack, styled } from "styled-system/jsx"
const plans = createListCollection({
items: [
{ label: "Free", value: "free", description: "For personal projects. 1 seat." },
{ label: "Pro", value: "pro", description: "For small teams. Up to 10 seats." },
{ label: "Enterprise", value: "enterprise", description: "Unlimited seats, SSO and audit logs." },
],
})
export function SelectItemDescriptionDemo() {
return (
<Select.Root collection={plans} maxW="xs">
<Select.HiddenSelect />
<Select.Label>Plan</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select plan" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.Item item={{ label: "Free", value: "free", description: "For personal projects. 1 seat." }}>
<Stack gap="0" flex="1">
<Select.ItemText>Free</Select.ItemText>
<styled.span textStyle="xs" color="fg.muted">
For personal projects. 1 seat.
</styled.span>
</Stack>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item={{ label: "Pro", value: "pro", description: "For small teams. Up to 10 seats." }}>
<Stack gap="0" flex="1">
<Select.ItemText>Pro</Select.ItemText>
<styled.span textStyle="xs" color="fg.muted">
For small teams. Up to 10 seats.
</styled.span>
</Stack>
<Select.ItemIndicator />
</Select.Item>
<Select.Item
item={{ label: "Enterprise", value: "enterprise", description: "Unlimited seats, SSO and audit logs." }}
>
<Stack gap="0" flex="1">
<Select.ItemText>Enterprise</Select.ItemText>
<styled.span textStyle="xs" color="fg.muted">
Unlimited seats, SSO and audit logs.
</styled.span>
</Stack>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}Searchable
Put Select.Search at the top of Select.Content, with the items in Select.List, and filter a useListCollection
with useFilter as people type. With a list, only the list scrolls and the search stays at the top. Add the icon with
InputGroup from @isbatak/react-ui/input-group. The arrow keys still move through the list and Enter picks the
highlighted item, while other keys go to the search. Focus the search when the list opens, clear the filter when it
closes, and show Select.Empty when nothing matches.
import { Portal } from "@ark-ui/react/portal"
import { InputGroup } from "@isbatak/react-ui/input-group"
import { Select, useFilter, useListCollection } from "@isbatak/react-ui/select"
import { useRef } from "react"
import { Icon } from "../../ui/icon"
const countries = [
"Argentina",
"Australia",
"Austria",
"Belgium",
"Brazil",
"Canada",
"Croatia",
"Denmark",
"Finland",
"France",
"Germany",
"Iceland",
"Ireland",
"Italy",
"Japan",
"Netherlands",
"Norway",
"Portugal",
"Spain",
"Sweden",
]
export function SelectSearchableDemo() {
const searchRef = useRef<HTMLInputElement>(null)
const { contains } = useFilter({ sensitivity: "base" })
const { collection, filter } = useListCollection({ initialItems: countries, filter: contains })
return (
<Select.Root
collection={collection}
maxW="xs"
onOpenChange={(details) => {
if (details.open) requestAnimationFrame(() => searchRef.current?.focus())
else filter("")
}}
>
<Select.HiddenSelect />
<Select.Label>Country</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select country" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content maxH="72">
<InputGroup startElement={<Icon name="search" />}>
<Select.Search
ref={searchRef}
placeholder="Search..."
aria-label="Search countries"
onChange={(event) => filter(event.currentTarget.value)}
/>
</InputGroup>
<Select.List>
{collection.items.map((country) => (
<Select.Item key={country} item={country}>
<Select.ItemText>{country}</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
))}
{collection.size === 0 && <Select.Empty>No countries found</Select.Empty>}
</Select.List>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}Fit
Set fit to size the trigger to its value instead of filling the width.
import { Portal } from "@ark-ui/react/portal"
import { createListCollection, Select } from "@isbatak/react-ui/select"
const frameworks = createListCollection({ items: ["React", "Vue", "Solid", "Svelte", "Angular"] })
export function SelectFitDemo() {
return (
<Select.Root collection={frameworks} fit defaultValue={["Solid"]} maxW="xs">
<Select.HiddenSelect />
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.Item item="React">
<Select.ItemText>React</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Vue">
<Select.ItemText>Vue</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Solid">
<Select.ItemText>Solid</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Svelte">
<Select.ItemText>Svelte</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Angular">
<Select.ItemText>Angular</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}Icon button
Render the trigger as any element with asChild, here an IconButton. Turn off sameWidth so the list can be wider
than the button.
import { Portal } from "@ark-ui/react/portal"
import { createListCollection, Select } from "@isbatak/react-ui/select"
import { IconButton } from "@isbatak/react-ui/button"
const sorts = createListCollection({ items: ["Newest", "Oldest", "Most popular"] })
function SortIcon() {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden>
<path d="M3 6h18M6 12h12M10 18h4" strokeLinecap="round" />
</svg>
)
}
export function SelectIconButtonDemo() {
return (
<Select.Root
collection={sorts}
defaultValue={["Newest"]}
positioning={{ sameWidth: false }}
w="auto"
>
<Select.HiddenSelect />
<Select.Control>
<Select.Trigger asChild>
<IconButton variant="outline" aria-label="Sort by">
<SortIcon />
</IconButton>
</Select.Trigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content minW="40">
<Select.Item item="Newest">
<Select.ItemText>Newest</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Oldest">
<Select.ItemText>Oldest</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Most popular">
<Select.ItemText>Most popular</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}Open from a popover
A select inside a popover can keep its Portal. Here it renders in place, which also works.
import { Portal } from "@ark-ui/react/portal"
import { createListCollection, Select } from "@isbatak/react-ui/select"
import { Button } from "@isbatak/react-ui/button"
import { Popover } from "@isbatak/react-ui/popover"
const frameworks = createListCollection({ items: ["React", "Vue", "Solid", "Svelte", "Angular"] })
export function SelectPopoverDemo() {
return (
<Popover.Root>
<Popover.Trigger asChild>
<Button variant="outline">Open popover</Button>
</Popover.Trigger>
<Portal>
<Popover.Positioner>
<Popover.Content>
<Popover.Header>
<Popover.Title>Project settings</Popover.Title>
</Popover.Header>
<Popover.Body>
<Select.Root collection={frameworks}>
<Select.HiddenSelect />
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Select.Positioner>
<Select.Content>
<Select.Item item="React">
<Select.ItemText>React</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Vue">
<Select.ItemText>Vue</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Solid">
<Select.ItemText>Solid</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Svelte">
<Select.ItemText>Svelte</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Angular">
<Select.ItemText>Angular</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Select.Root>
</Popover.Body>
</Popover.Content>
</Popover.Positioner>
</Portal>
</Popover.Root>
)
}Open from a drawer
Leave out the Portal when the select is inside a drawer or dialog, so the list stays in the drawer's layer and focus
trap.
import { Portal } from "@ark-ui/react/portal"
import { createListCollection, Select } from "@isbatak/react-ui/select"
import { Button } from "@isbatak/react-ui/button"
import { Drawer } from "@isbatak/react-ui/drawer"
const frameworks = createListCollection({ items: ["React", "Vue", "Solid", "Svelte", "Angular"] })
export function SelectDrawerDemo() {
return (
<Drawer.Root placement="bottom">
<Drawer.Trigger asChild>
<Button variant="outline">Open drawer</Button>
</Drawer.Trigger>
<Portal>
<Drawer.Backdrop />
<Drawer.Positioner>
<Drawer.Content>
<Drawer.Header>
<Drawer.Title>Project settings</Drawer.Title>
</Drawer.Header>
<Drawer.Body pb="6">
<Select.Root collection={frameworks}>
<Select.HiddenSelect />
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Select.Positioner>
<Select.Content>
<Select.Item item="React">
<Select.ItemText>React</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Vue">
<Select.ItemText>Vue</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Solid">
<Select.ItemText>Solid</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Svelte">
<Select.ItemText>Svelte</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
<Select.Item item="Angular">
<Select.ItemText>Angular</Select.ItemText>
<Select.ItemIndicator />
</Select.Item>
</Select.Content>
</Select.Positioner>
</Select.Root>
</Drawer.Body>
</Drawer.Content>
</Drawer.Positioner>
</Portal>
</Drawer.Root>
)
}Recipe
| Variant | Values | Default |
|---|---|---|
variant | outline, subtle, ghost | outline |
size | xs, sm, md, lg | md |
fit | true |
| Variable | Description |
|---|---|
--select-trigger-height | The trigger's minimum height, set by size |
--select-trigger-padding-x | The trigger's horizontal padding, set by size |
--input-border-color | The trigger border, colors.border |
--input-border-hover-color | The border on hover, colors.border.emphasized |
--input-border-focus-color | The border on focus and while open |
--input-border-error-color | The border when invalid |