'use client'
import { createListCollection } from '@chakra-ui/react'
import { Select } from '@saas-ui/react'
export const SelectBasic = () => {
return (
<Select.Root collection={frameworks} size="sm" width="320px">
<Select.Label>Select framework</Select.Label>
<Select.Trigger>
<Select.ValueText placeholder="Select movie" />
</Select.Trigger>
<Select.Content>
{frameworks.items.map((movie) => (
<Select.Item item={movie} key={movie.value}>
{movie.label}
</Select.Item>
))}
</Select.Content>
</Select.Root>
)
}
const frameworks = createListCollection({
items: [
{ label: 'React.js', value: 'react' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
],
})
import { Select } from '@saas-ui/react/select'
<Select.Root items={[]}>
<Select.Label />
<Select.Trigger>
<Select.ValueText placeholder="Select" />
</Select.Trigger>
<Select.Content>
<Select.Item />
</Select.Content>
</Select.Root>
Use the size
prop to change the size of the select component.
'use client'
import { For, Stack, createListCollection } from '@chakra-ui/react'
import { Select } from '@saas-ui/react'
export const SelectWithSizes = () => {
return (
<Stack gap="5" width="320px">
<For each={['xs', 'sm', 'md', 'lg']}>
{(size) => (
<Select.Root key={size} size={size} collection={frameworks}>
<Select.Label>size = {size}</Select.Label>
<Select.Trigger>
<Select.ValueText placeholder="Select movie" />
</Select.Trigger>
<Select.Content>
{frameworks.items.map((movie) => (
<Select.Item item={movie} key={movie.value}>
{movie.label}
</Select.Item>
))}
</Select.Content>
</Select.Root>
)}
</For>
</Stack>
)
}
const frameworks = createListCollection({
items: [
{ label: 'React.js', value: 'react' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
],
})
Use the variant
prop to change the appearance of the select component.
'use client'
import { For, Stack, createListCollection } from '@chakra-ui/react'
import { Select } from '@saas-ui/react'
export const SelectWithVariants = () => {
return (
<Stack gap="5" width="320px">
<For each={['outline', 'subtle']}>
{(variant) => (
<Select.Root key={variant} variant={variant} collection={frameworks}>
<Select.Label>Select framework - {variant}</Select.Label>
<Select.Trigger>
<Select.ValueText placeholder="Select movie" />
</Select.Trigger>
<Select.Content>
{frameworks.items.map((movie) => (
<Select.Item item={movie} key={movie.value}>
{movie.label}
</Select.Item>
))}
</Select.Content>
</Select.Root>
)}
</For>
</Stack>
)
}
const frameworks = createListCollection({
items: [
{ label: 'React.js', value: 'react' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
],
})
Use the SelectItemGroup
component to group select options.
'use client'
import { createListCollection } from '@chakra-ui/react'
import { Select } from '@saas-ui/react'
export const SelectWithOptionGroup = () => {
return (
<Select.Root collection={frameworks} size="sm" width="320px">
<Select.Label>Select framework</Select.Label>
<Select.Trigger>
<Select.ValueText placeholder="Select movie" />
</Select.Trigger>
<Select.Content>
{categories.map((category) => (
<Select.ItemGroup key={category.group} label={category.group}>
{category.items.map((item) => (
<Select.Item item={item} key={item.value}>
{item.label}
</Select.Item>
))}
</Select.ItemGroup>
))}
</Select.Content>
</Select.Root>
)
}
const frameworks = createListCollection({
items: [
{ label: 'Naruto', value: 'naruto', group: 'Anime' },
{ label: 'One Piece', value: 'one-piece', group: 'Anime' },
{ label: 'Dragon Ball', value: 'dragon-ball', group: 'Anime' },
{
label: 'The Shawshank Redemption',
value: 'the-shawshank-redemption',
group: 'Movies',
},
{ label: 'The Godfather', value: 'the-godfather', group: 'Movies' },
{ label: 'The Dark Knight', value: 'the-dark-knight', group: 'Movies' },
],
})
const categories = frameworks.items.reduce(
(acc, item) => {
const group = acc.find((group) => group.group === item.group)
if (group) {
group.items.push(item)
} else {
acc.push({ group: item.group, items: [item] })
}
return acc
},
[] as { group: string; items: (typeof frameworks)['items'] }[],
)
Use the value
and onValueChange
props to control the select component.
'use client'
import { useState } from 'react'
import { createListCollection } from '@chakra-ui/react'
import { Select } from '@saas-ui/react'
export const SelectControlled = () => {
const [value, setValue] = useState<string[]>([])
return (
<Select.Root
collection={frameworks}
width="320px"
value={value}
onValueChange={(e) => setValue(e.value)}
>
<Select.Label>Select framework</Select.Label>
<Select.Trigger>
<Select.ValueText placeholder="Select movie" />
</Select.Trigger>
<Select.Content>
{frameworks.items.map((movie) => (
<Select.Item item={movie} key={movie.value}>
{movie.label}
</Select.Item>
))}
</Select.Content>
</Select.Root>
)
}
const frameworks = createListCollection({
items: [
{ label: 'React.js', value: 'react' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
],
})
Here's an example of how to use the Select
component with react-hook-form
.
'use client'
import { Button, Stack, createListCollection } from '@chakra-ui/react'
import { zodResolver } from '@hookform/resolvers/zod'
import { Field, Select } from '@saas-ui/react'
import { Controller, useForm } from 'react-hook-form'
import { z } from 'zod'
const formSchema = z.object({
framework: z.string({ message: 'Framework is required' }).array(),
})
type FormValues = z.infer<typeof formSchema>
export const SelectWithHookForm = () => {
const {
handleSubmit,
formState: { errors },
control,
} = useForm<FormValues>({
resolver: zodResolver(formSchema),
})
const onSubmit = handleSubmit((data) => console.log(data))
return (
<form onSubmit={onSubmit}>
<Stack gap="4" align="flex-start">
<Controller
control={control}
name="framework"
render={({ field }) => (
<Field.Root invalid={!!errors.framework} width="320px">
<Field.Label>Framework</Field.Label>
<Select.Root
name={field.name}
value={field.value}
onValueChange={({ value }) => field.onChange(value)}
onInteractOutside={() => field.onBlur()}
collection={frameworks}
>
<Select.Trigger>
<Select.ValueText placeholder="Select movie" />
</Select.Trigger>
<Select.Content>
{frameworks.items.map((movie) => (
<Select.Item item={movie} key={movie.value}>
{movie.label}
</Select.Item>
))}
</Select.Content>
</Select.Root>
<Field.ErrorText>{errors.framework?.message}</Field.ErrorText>
</Field.Root>
)}
/>
<Button size="sm" type="submit">
Submit
</Button>
</Stack>
</form>
)
}
const frameworks = createListCollection({
items: [
{ label: 'React.js', value: 'react' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
],
})
Use the disabled
prop to disable the select component.
'use client'
import { createListCollection } from '@chakra-ui/react'
import { Select } from '@saas-ui/react'
export const SelectWithDisabled = () => {
return (
<Select.Root disabled collection={frameworks} size="sm" width="320px">
<Select.Label>Select framework</Select.Label>
<Select.Trigger>
<Select.ValueText placeholder="Select movie" />
</Select.Trigger>
<Select.Content>
{frameworks.items.map((movie) => (
<Select.Item item={movie} key={movie.value}>
{movie.label}
</Select.Item>
))}
</Select.Content>
</Select.Root>
)
}
const frameworks = createListCollection({
items: [
{ label: 'React.js', value: 'react' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
],
})
Use the invalid
prop to indicate that the select component has an error.
'use client'
import { createListCollection } from '@chakra-ui/react'
import { Field, Select } from '@saas-ui/react'
export const SelectWithInvalid = () => {
return (
<Field.Root invalid>
<Select.Root collection={frameworks} size="sm" width="320px">
<Select.Label>Select framework</Select.Label>
<Select.Trigger>
<Select.ValueText placeholder="Select movie" />
</Select.Trigger>
<Select.Content>
{frameworks.items.map((movie) => (
<Select.Item item={movie} key={movie.value}>
{movie.label}
</Select.Item>
))}
</Select.Content>
</Select.Root>
<Field.ErrorText>This is an error</Field.ErrorText>
</Field.Root>
)
}
const frameworks = createListCollection({
items: [
{ label: 'React.js', value: 'react' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
],
})
Use the positioning
prop to control the underlying floating-ui
options of
the select component.
'use client'
import { createListCollection } from '@chakra-ui/react'
import { Select } from '@saas-ui/react'
export const SelectWithPositioning = () => {
return (
<Select.Root
collection={frameworks}
size="sm"
width="320px"
positioning={{ placement: 'top', flip: false }}
>
<Select.Label>Select framework</Select.Label>
<Select.Trigger>
<Select.ValueText placeholder="Select movie" />
</Select.Trigger>
<Select.Content>
{frameworks.items.map((movie) => (
<Select.Item item={movie} key={movie.value}>
{movie.label}
</Select.Item>
))}
</Select.Content>
</Select.Root>
)
}
const frameworks = createListCollection({
items: [
{ label: 'React.js', value: 'react' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
],
})
Use the Select
within a Popover
component.
'use client'
import { createListCollection } from '@chakra-ui/react'
import { Button, Popover, Select } from '@saas-ui/react'
export const SelectInPopover = () => {
return (
<Popover.Root size="xs">
<Popover.Trigger asChild>
<Button variant="outline" size="sm">
Select in Popover
</Button>
</Popover.Trigger>
<Popover.Content>
<Popover.Header>Select in Popover</Popover.Header>
<Popover.Body>
<Select.Root
collection={frameworks}
size="sm"
positioning={{ sameWidth: true, placement: 'bottom' }}
>
<Select.Trigger>
<Select.ValueText placeholder="Select" />
</Select.Trigger>
<Select.Content portalled={false} width="full">
{frameworks.items.map((item) => (
<Select.Item item={item} key={item.value}>
{item.label}
</Select.Item>
))}
</Select.Content>
</Select.Root>
</Popover.Body>
</Popover.Content>
</Popover.Root>
)
}
const frameworks = createListCollection({
items: [
{ label: 'React.js', value: 'react' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
],
})
Here's an example of how to compose the Select
and the Avatar
.
'use client'
import { HStack, createListCollection } from '@chakra-ui/react'
import { Avatar, Select } from '@saas-ui/react'
const SelectValueItem = () => (
<Select.ValueText placeholder="Select movie">
{(items: Array<{ name: string; avatar: string }>) => {
const { name, avatar } = items[0]
return (
<HStack>
<Avatar name={name} size="xs" src={avatar} />
{name}
</HStack>
)
}}
</Select.ValueText>
)
export const SelectWithAvatar = () => {
return (
<Select.Root
collection={members}
size="sm"
width="240px"
defaultValue={['jessica_jones']}
positioning={{ sameWidth: true }}
>
<Select.Label>Select member</Select.Label>
<Select.Trigger>
<SelectValueItem />
</Select.Trigger>
<Select.Content portalled={false}>
{members.items.map((item) => (
<Select.Item item={item} key={item.id} justifyContent="flex-start">
<Avatar name={item.name} src={item.avatar} size="xs" />
{item.name}
</Select.Item>
))}
</Select.Content>
</Select.Root>
)
}
const members = createListCollection({
items: [
{
name: 'Jessica Jones',
id: 'jessica_jones',
avatar:
'https://images.unsplash.com/photo-1531746020798-e6953c6e8e04?w=100',
},
{
name: 'Kenneth Johnson',
id: 'kenneth_johnson',
avatar:
'https://images.unsplash.com/photo-1523477800337-966dbabe060b?w=100',
},
{
name: 'Kate Wilson',
id: 'kate_wilson',
avatar:
'https://images.unsplash.com/photo-1609712409631-dbbb050746d1?w=100',
},
],
itemToString: (item) => item.name,
itemToValue: (item) => item.id,
})
Pass the clearable
prop to the SelectTrigger
.
'use client'
import { createListCollection } from '@chakra-ui/react'
import { Select } from '@saas-ui/react'
export const SelectWithClear = () => {
return (
<Select.Root
collection={animeMovies}
defaultValue={['spirited_away']}
size="sm"
width="320px"
>
<Select.Label>Select fav. anime</Select.Label>
<Select.Trigger clearable>
<Select.ValueText placeholder="Select movie" />
</Select.Trigger>
<Select.Content>
{animeMovies.items.map((movie) => (
<Select.Item item={movie} key={movie.value}>
{movie.label}
</Select.Item>
))}
</Select.Content>
</Select.Root>
)
}
const animeMovies = createListCollection({
items: [
{ label: 'Spirited Away', value: 'spirited_away' },
{ label: 'My Neighbor Totoro', value: 'my_neighbor_totoro' },
{ label: 'Akira', value: 'akira' },
{ label: 'Princess Mononoke', value: 'princess_mononoke' },
{ label: 'Grave of the Fireflies', value: 'grave_of_the_fireflies' },
{ label: "Howl's Moving Castle", value: 'howls_moving_castle' },
{ label: 'Ghost in the Shell', value: 'ghost_in_the_shell' },
{ label: 'Naruto', value: 'naruto' },
{ label: 'Hunter x Hunter', value: 'hunter_x_hunter' },
{ label: 'The Wind Rises', value: 'the_wind_rises' },
{ label: "Kiki's Delivery Service", value: 'kikis_delivery_service' },
{ label: 'Perfect Blue', value: 'perfect_blue' },
{
label: 'The Girl Who Leapt Through Time',
value: 'the_girl_who_leapt_through_time',
},
{ label: 'Weathering with You', value: 'weathering_with_you' },
{ label: 'Ponyo', value: 'ponyo' },
{ label: '5 Centimeters per Second', value: '5_centimeters_per_second' },
{ label: 'A Silent Voice', value: 'a_silent_voice' },
{ label: 'Paprika', value: 'paprika' },
{ label: 'Wolf Children', value: 'wolf_children' },
{ label: 'Redline', value: 'redline' },
{
label: 'The Tale of the Princess Kaguya',
value: 'the_tale_of_the_princess_kaguya',
},
],
})
'use client'
import { createListCollection } from '@chakra-ui/react'
import { Select } from '@saas-ui/react'
export const SelectWithOverflow = () => {
return (
<Select.Root collection={animeMovies} size="sm" width="240px">
<Select.Label>Select anime</Select.Label>
<Select.Trigger>
<Select.ValueText placeholder="Select movie" />
</Select.Trigger>
<Select.Content>
{animeMovies.items.map((movie) => (
<Select.Item item={movie} key={movie.value}>
{movie.label}
</Select.Item>
))}
</Select.Content>
</Select.Root>
)
}
const animeMovies = createListCollection({
items: [
{ label: 'Spirited Away', value: 'spirited_away' },
{ label: 'My Neighbor Totoro', value: 'my_neighbor_totoro' },
{ label: 'Akira', value: 'akira' },
{ label: 'Princess Mononoke', value: 'princess_mononoke' },
{ label: 'Grave of the Fireflies', value: 'grave_of_the_fireflies' },
{ label: "Howl's Moving Castle", value: 'howls_moving_castle' },
{ label: 'Ghost in the Shell', value: 'ghost_in_the_shell' },
{ label: 'Naruto', value: 'naruto' },
{ label: 'Hunter x Hunter', value: 'hunter_x_hunter' },
{ label: 'The Wind Rises', value: 'the_wind_rises' },
{ label: "Kiki's Delivery Service", value: 'kikis_delivery_service' },
{ label: 'Perfect Blue', value: 'perfect_blue' },
{
label: 'The Girl Who Leapt Through Time',
value: 'the_girl_who_leapt_through_time',
},
{ label: 'Weathering with You', value: 'weathering_with_you' },
{ label: 'Ponyo', value: 'ponyo' },
{ label: '5 Centimeters per Second', value: '5_centimeters_per_second' },
{ label: 'A Silent Voice', value: 'a_silent_voice' },
{ label: 'Paprika', value: 'paprika' },
{ label: 'Wolf Children', value: 'wolf_children' },
{ label: 'Redline', value: 'redline' },
{
label: 'The Tale of the Princess Kaguya',
value: 'the_tale_of_the_princess_kaguya',
},
],
})
Prop | Default | Type |
---|---|---|
items * | T[] | readonly T[] The options of the select | |
closeOnSelect | true | boolean Whether the select should close after an item is selected |
composite | true | boolean Whether the select is a composed with other composite widgets like tabs or combobox |
lazyMount | false | boolean Whether to enable lazy mounting |
loopFocus | false | boolean Whether to loop the keyboard navigation through the options |
unmountOnExit | false | boolean Whether to unmount on exit. |
colorPalette | 'gray' | 'gray' | 'red' | 'orange' | 'yellow' | 'green' | 'teal' | 'blue' | 'cyan' | 'purple' | 'pink' | 'accent' The color palette of the component |
variant | 'outline' | 'outline' | 'filled' The variant of the component |
size | 'md' | 'xs' | 'sm' | 'md' | 'lg' The size of the component |
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
defaultOpen | boolean The initial open state of the select when it is first rendered. Use when you do not need to control its open state. | |
defaultValue | string[] The initial value of the select when it is first rendered. Use when you do not need to control the state of the select. | |
disabled | boolean Whether the select is disabled | |
form | string The associate form of the underlying select. | |
highlightedValue | string The key of the highlighted item | |
id | string The unique identifier of the machine. | |
ids | Partial<{
root: string
content: string
control: string
trigger: string
clearTrigger: string
label: string
hiddenSelect: string
positioner: string
item(id: string | number): string
itemGroup(id: string | number): string
itemGroupLabel(id: string | number): string
}> The ids of the elements in the select. Useful for composition. | |
immediate | boolean Whether to synchronize the present change immediately or defer it to the next frame | |
invalid | boolean Whether the select is invalid | |
isItemDisabled | (item: T) => boolean Whether the item is disabled | |
itemToString | (item: T) => string The label of the item | |
itemToValue | (item: T) => string The value of the item | |
multiple | boolean Whether to allow multiple selection | |
name | string The `name` attribute of the underlying select. | |
onExitComplete | () => void Function called when the animation ends in the closed state | |
onFocusOutside | (event: FocusOutsideEvent) => void Function called when the focus is moved outside the component | |
onHighlightChange | (details: HighlightChangeDetails<T>) => void The callback fired when the highlighted item changes. | |
onInteractOutside | (event: InteractOutsideEvent) => void Function called when an interaction happens outside the component | |
onOpenChange | (details: OpenChangeDetails) => void Function called when the popup is opened | |
onPointerDownOutside | (event: PointerDownOutsideEvent) => void Function called when the pointer is pressed down outside the component | |
onValueChange | (details: ValueChangeDetails<T>) => void The callback fired when the selected item changes. | |
open | boolean Whether the select menu is open | |
positioning | PositioningOptions The positioning options of the menu. | |
present | boolean Whether the node is present (controlled by the user) | |
readOnly | boolean Whether the select is read-only | |
required | boolean Whether the select is required | |
scrollToIndexFn | (details: ScrollToIndexDetails) => void Function to scroll to a specific index | |
value | string[] The keys of the selected items | |
as | React.ElementType The underlying element to render. | |
unstyled | boolean Whether to remove the component's style. |