"use client"
import { Portal, Select, createListCollection } from "@chakra-ui/react"
const Demo = () => {
return (
<Select.Root collection={frameworks} size="sm" width="320px">
<Select.HiddenSelect />
<Select.Label>Select 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>
{frameworks.items.map((framework) => (
<Select.Item item={framework} key={framework.value}>
{framework.label}
<Select.ItemIndicator />
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}
const frameworks = createListCollection({
items: [
{ label: "React.js", value: "react" },
{ label: "Vue.js", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
],
})
Usage
import { Select } from "@chakra-ui/react"
<Select.Root>
<Select.HiddenSelect />
<Select.Label />
<Select.Control>
<Select.Trigger>
<Select.ValueText />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
<Select.ClearTrigger />
</Select.IndicatorGroup>
</Select.Control>
<Select.Positioner>
<Select.Content>
<Select.Item />
<Select.ItemGroup>
<Select.ItemGroupLabel />
<Select.Item />
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Select.Root>
Examples
Sizes
Use the size
prop to change the size of the select component.
"use client"
import {
For,
Portal,
Select,
Stack,
createListCollection,
} from "@chakra-ui/react"
const Demo = () => {
return (
<Stack gap="5" width="320px">
<For each={["xs", "sm", "md", "lg"]}>
{(size) => (
<Select.Root key={size} size={size} collection={frameworks}>
<Select.HiddenSelect />
<Select.Label>size = {size}</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>
{frameworks.items.map((framework) => (
<Select.Item item={framework} key={framework.value}>
{framework.label}
<Select.ItemIndicator />
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
</Portal>
</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" },
],
})
Variants
Use the variant
prop to change the appearance of the select component.
"use client"
import {
For,
Portal,
Select,
Stack,
createListCollection,
} from "@chakra-ui/react"
const Demo = () => {
return (
<Stack gap="5" width="320px">
<For each={["outline", "subtle"]}>
{(variant) => (
<Select.Root key={variant} variant={variant} collection={frameworks}>
<Select.HiddenSelect />
<Select.Label>Select framework - {variant}</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>
{frameworks.items.map((framework) => (
<Select.Item item={framework} key={framework.value}>
{framework.label}
<Select.ItemIndicator />
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
</Portal>
</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" },
],
})
Option Group
Use the Select.ItemGroup
component to group select options.
"use client"
import { Portal, Select, createListCollection } from "@chakra-ui/react"
import { groupBy } from "es-toolkit"
const Demo = () => {
return (
<Select.Root collection={collection} size="sm" width="320px">
<Select.HiddenSelect />
<Select.Label>Select 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>
{categories.map(([category, items]) => (
<Select.ItemGroup key={category}>
<Select.ItemGroupLabel>{category}</Select.ItemGroupLabel>
{items.map((item) => (
<Select.Item item={item} key={item.value}>
{item.label}
<Select.ItemIndicator />
</Select.Item>
))}
</Select.ItemGroup>
))}
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}
const collection = createListCollection({
items: [
{ label: "Naruto", value: "naruto", category: "Anime" },
{ label: "One Piece", value: "one-piece", category: "Anime" },
{ label: "Dragon Ball", value: "dragon-ball", category: "Anime" },
{
label: "The Shawshank Redemption",
value: "the-shawshank-redemption",
category: "Movies",
},
{ label: "The Godfather", value: "the-godfather", category: "Movies" },
{ label: "The Dark Knight", value: "the-dark-knight", category: "Movies" },
],
})
const categories = Object.entries(
groupBy(collection.items, (item) => item.category),
)
Controlled
Use the value
and onValueChange
props to control the select component.
"use client"
import { Portal, Select, createListCollection } from "@chakra-ui/react"
import { useState } from "react"
const Demo = () => {
const [value, setValue] = useState<string[]>([])
return (
<Select.Root
collection={frameworks}
width="320px"
value={value}
onValueChange={(e) => setValue(e.value)}
>
<Select.HiddenSelect />
<Select.Label>Select 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>
{frameworks.items.map((framework) => (
<Select.Item item={framework} key={framework.value}>
{framework.label}
<Select.ItemIndicator />
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}
const frameworks = createListCollection({
items: [
{ label: "React.js", value: "react" },
{ label: "Vue.js", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
],
})
Async Loading
Here's an example of how to populate the select collection
from a remote
source.
"use client"
import { Portal, Select, Spinner, createListCollection } from "@chakra-ui/react"
import { useMemo } from "react"
import { useAsync } from "react-use"
interface Pokemon {
name: string
url: string
}
const Demo = () => {
const state = useAsync(async (): Promise<Pokemon[]> => {
const response = await fetch("https://pokeapi.co/api/v2/pokemon")
const data = await response.json()
return data.results
}, [])
const collection = useMemo(() => {
return createListCollection({
items: state.value ?? [],
itemToString: (pokemon) => pokemon.name,
itemToValue: (pokemon) => pokemon.name,
})
}, [state.value])
return (
<Select.Root collection={collection} size="sm" width="320px">
<Select.HiddenSelect />
<Select.Label>Select pokemon</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select pokemon" />
</Select.Trigger>
<Select.IndicatorGroup>
{state.loading && (
<Spinner size="xs" borderWidth="1.5px" color="fg.muted" />
)}
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
{collection.items.map((pokemon) => (
<Select.Item item={pokemon} key={pokemon.name}>
{pokemon.name}
<Select.ItemIndicator />
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}
Hook Form
Here's an example of how to use the Select
component with react-hook-form
.
"use client"
import {
Button,
Field,
Portal,
Select,
Stack,
createListCollection,
} from "@chakra-ui/react"
import { zodResolver } from "@hookform/resolvers/zod"
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>
const Demo = () => {
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">
<Field.Root invalid={!!errors.framework} width="320px">
<Field.Label>Framework</Field.Label>
<Controller
control={control}
name="framework"
render={({ field }) => (
<Select.Root
name={field.name}
value={field.value}
onValueChange={({ value }) => field.onChange(value)}
onInteractOutside={() => field.onBlur()}
collection={frameworks}
>
<Select.HiddenSelect />
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
{frameworks.items.map((framework) => (
<Select.Item item={framework} key={framework.value}>
{framework.label}
<Select.ItemIndicator />
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
</Portal>
</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" },
],
})
Disabled
Use the disabled
prop to disable the select component.
"use client"
import { Portal, Select, createListCollection } from "@chakra-ui/react"
const Demo = () => {
return (
<Select.Root disabled collection={frameworks} size="sm" width="320px">
<Select.HiddenSelect />
<Select.Label>Select 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>
{frameworks.items.map((framework) => (
<Select.Item item={framework} key={framework.value}>
{framework.label}
<Select.ItemIndicator />
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}
const frameworks = createListCollection({
items: [
{ label: "React.js", value: "react" },
{ label: "Vue.js", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
],
})
Invalid
Here's an example of how to compose the Select
component with the Field
component to display an error state.
"use client"
import { Field, Portal, Select, createListCollection } from "@chakra-ui/react"
const Demo = () => {
return (
<Field.Root invalid>
<Select.Root collection={frameworks} size="sm" width="320px">
<Select.HiddenSelect />
<Select.Label>Select 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>
{frameworks.items.map((framework) => (
<Select.Item item={framework} key={framework.value}>
{framework.label}
<Select.ItemIndicator />
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
</Portal>
</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" },
],
})
Multiple
Use the multiple
prop to allow multiple selections.
"use client"
import { Portal, Select, createListCollection } from "@chakra-ui/react"
const Demo = () => {
return (
<Select.Root multiple collection={frameworks} size="sm" width="320px">
<Select.HiddenSelect />
<Select.Label>Select 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>
{frameworks.items.map((framework) => (
<Select.Item item={framework} key={framework.value}>
{framework.label}
<Select.ItemIndicator />
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}
const frameworks = createListCollection({
items: [
{ label: "React.js", value: "react" },
{ label: "Vue.js", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
],
})
Positioning
Use the positioning
prop to control the underlying floating-ui
options of
the select component.
"use client"
import { Portal, Select, createListCollection } from "@chakra-ui/react"
const Demo = () => {
return (
<Select.Root
collection={frameworks}
size="sm"
width="320px"
positioning={{ placement: "top", flip: false }}
>
<Select.HiddenSelect />
<Select.Label>Select 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>
{frameworks.items.map((framework) => (
<Select.Item item={framework} key={framework.value}>
{framework.label}
<Select.ItemIndicator />
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}
const frameworks = createListCollection({
items: [
{ label: "React.js", value: "react" },
{ label: "Vue.js", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
],
})
Clear Trigger
Render the Select.ClearTrigger
component to show a clear button. Clicking the
clear button will clear the selected value.
"use client"
import { Portal, Select, createListCollection } from "@chakra-ui/react"
const Demo = () => {
return (
<Select.Root
collection={animeMovies}
defaultValue={["spirited_away"]}
size="sm"
width="320px"
>
<Select.HiddenSelect />
<Select.Label>Select fav. anime</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select anime" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.ClearTrigger />
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
{animeMovies.items.map((anime) => (
<Select.Item item={anime} key={anime.value}>
{anime.label}
<Select.ItemIndicator />
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
</Portal>
</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",
},
],
})
Overflow
When the options are too many, the options will overflow the container due to
the maxHeight
set.
"use client"
import { Portal, Select, createListCollection } from "@chakra-ui/react"
const Demo = () => {
return (
<Select.Root collection={animeMovies} size="sm" width="240px">
<Select.HiddenSelect />
<Select.Label>Select anime</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select movie" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
{animeMovies.items.map((movie) => (
<Select.Item item={movie} key={movie.value}>
{movie.label}
<Select.ItemIndicator />
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
</Portal>
</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",
},
],
})
Item Description
Here's an example of how to render a description for each item.
"use client"
import {
Portal,
Select,
Span,
Stack,
createListCollection,
} from "@chakra-ui/react"
const Demo = () => {
return (
<Select.Root
collection={frameworks}
size="sm"
width="320px"
defaultValue={["pro"]}
>
<Select.HiddenSelect />
<Select.Label>Select 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>
{frameworks.items.map((framework) => (
<Select.Item item={framework} key={framework.value}>
<Stack gap="0">
<Select.ItemText>{framework.label}</Select.ItemText>
<Span color="fg.muted" textStyle="xs">
{framework.description}
</Span>
</Stack>
<Select.ItemIndicator />
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}
const frameworks = createListCollection({
items: [
{
label: "Basic Plan",
value: "basic",
description: "$9/month - Perfect for small projects",
},
{
label: "Pro Plan",
value: "pro",
description: "$29/month - Advanced features",
},
{
label: "Business Plan",
value: "business",
description: "$99/month - Enterprise-grade solutions",
},
{
label: "Enterprise Plan",
value: "enterprise",
description: "Custom pricing - Tailored solutions",
},
],
})
Within Popover
Here's an example of how to use the Select
within a Popover
component.
"use client"
import {
Button,
Popover,
Portal,
Select,
createListCollection,
} from "@chakra-ui/react"
const Demo = () => {
return (
<Popover.Root size="xs">
<Popover.Trigger asChild>
<Button variant="outline" size="sm">
Select in Popover
</Button>
</Popover.Trigger>
<Portal>
<Popover.Positioner>
<Popover.Content>
<Popover.Header>Select in Popover</Popover.Header>
<Popover.Body>
<Select.Root
collection={frameworks}
size="sm"
positioning={{ sameWidth: true, placement: "bottom" }}
>
<Select.HiddenSelect />
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select framework" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Select.Positioner>
<Select.Content width="full">
{frameworks.items.map((item) => (
<Select.Item item={item} key={item.value}>
{item.label}
<Select.ItemIndicator />
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
</Select.Root>
</Popover.Body>
</Popover.Content>
</Popover.Positioner>
</Portal>
</Popover.Root>
)
}
const frameworks = createListCollection({
items: [
{ label: "React.js", value: "react" },
{ label: "Vue.js", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
],
})
Within Dialog
To use the Select
within a Dialog
, you need to avoid portalling the
Select.Positioner
to the document's body.
-<Portal>
<Select.Positioner>
<Select.Content>
{/* ... */}
</Select.Content>
</Select.Positioner>
-</Portal>
If you have set scrollBehavior="inside"
on the Dialog
, you need to:
- Set the select positioning to
fixed
to avoid the select from being clipped by the dialog. - Set
hideWhenDetached
totrue
to hide the select when the trigger is scrolled out of view.
<Select.Root positioning={{ strategy: "fixed", hideWhenDetached: true }}>
{/* ... */}
</Select.Root>
"use client"
import {
Button,
CloseButton,
Dialog,
Portal,
Select,
createListCollection,
} from "@chakra-ui/react"
const Demo = () => {
return (
<Dialog.Root>
<Dialog.Trigger asChild>
<Button variant="outline">Open Dialog</Button>
</Dialog.Trigger>
<Portal>
<Dialog.Backdrop />
<Dialog.Positioner>
<Dialog.Content>
<Dialog.CloseTrigger asChild>
<CloseButton />
</Dialog.CloseTrigger>
<Dialog.Header>
<Dialog.Title>Select in Dialog</Dialog.Title>
</Dialog.Header>
<Dialog.Body>
<DialogSelect />
</Dialog.Body>
<Dialog.Footer />
</Dialog.Content>
</Dialog.Positioner>
</Portal>
</Dialog.Root>
)
}
const frameworks = createListCollection({
items: [
{ label: "React.js", value: "react" },
{ label: "Vue.js", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
],
})
function DialogSelect() {
return (
<Select.Root collection={frameworks} size="sm">
<Select.HiddenSelect />
<Select.Label>Select 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>
{frameworks.items.map((item) => (
<Select.Item item={item} key={item.value}>
{item.label}
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
</Select.Root>
)
}
Avatar Select
Here's an example of how to compose the Select
and the Avatar
.
"use client"
import {
Avatar,
HStack,
Select,
createListCollection,
useSelectContext,
} from "@chakra-ui/react"
const SelectValue = () => {
const select = useSelectContext()
const items = select.selectedItems as Array<{ name: string; avatar: string }>
const { name, avatar } = items[0]
return (
<Select.ValueText placeholder="Select member">
<HStack>
<Avatar.Root shape="rounded" size="2xs">
<Avatar.Image src={avatar} alt={name} />
<Avatar.Fallback name={name} />
</Avatar.Root>
{name}
</HStack>
</Select.ValueText>
)
}
const Demo = () => {
return (
<Select.Root
collection={members}
size="sm"
width="240px"
defaultValue={["jessica_jones"]}
positioning={{ sameWidth: true }}
>
<Select.HiddenSelect />
<Select.Label>Select member</Select.Label>
<Select.Control>
<Select.Trigger>
<SelectValue />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Select.Positioner>
<Select.Content>
{members.items.map((item) => (
<Select.Item item={item} key={item.id} justifyContent="flex-start">
<Avatar.Root shape="rounded" size="2xs">
<Avatar.Image src={item.avatar} alt={item.name} />
<Avatar.Fallback name={item.name} />
</Avatar.Root>
{item.name}
<Select.ItemIndicator />
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
</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,
})
Country Select
Here's an example of how to use the Select
component to select a country.
"use client"
import { Portal, Select, createListCollection } from "@chakra-ui/react"
import { groupBy } from "es-toolkit"
const Demo = () => {
return (
<Select.Root
collection={countries}
size="sm"
width="320px"
defaultValue={["NG"]}
>
<Select.HiddenSelect />
<Select.Label>Select country</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="-" />
</Select.Trigger>
<Select.IndicatorGroup>
<Select.Indicator />
</Select.IndicatorGroup>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
{continents.map(([continent, items]) => (
<Select.ItemGroup key={continent}>
<Select.ItemGroupLabel>{continent}</Select.ItemGroupLabel>
{items.map((item) => (
<Select.Item item={item} key={item.value}>
{countries.stringifyItem(item)}
<Select.ItemIndicator />
</Select.Item>
))}
</Select.ItemGroup>
))}
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}
const countries = createListCollection({
items: [
{ value: "US", label: "United States", flag: "๐บ๐ธ", continent: "America" },
{ value: "CA", label: "Canada", flag: "๐จ๐ฆ", continent: "America" },
{ value: "MX", label: "Mexico", flag: "๐ฒ๐ฝ", continent: "America" },
{ value: "BR", label: "Brazil", flag: "๐ง๐ท", continent: "America" },
{ value: "ZA", label: "South Africa", flag: "๐ฟ๐ฆ", continent: "Africa" },
{ value: "NG", label: "Nigeria", flag: "๐ณ๐ฌ", continent: "Africa" },
{ value: "MA", label: "Morocco", flag: "๐ฒ๐ฆ", continent: "Africa" },
{ value: "EG", label: "Egypt", flag: "๐ช๐ฌ", continent: "Africa" },
{ value: "CN", label: "China", flag: "๐จ๐ณ", continent: "Asia" },
{ value: "JP", label: "Japan", flag: "๐ฏ๐ต", continent: "Asia" },
{ value: "IN", label: "India", flag: "๐ฎ๐ณ", continent: "Asia" },
{ value: "KR", label: "South Korea", flag: "๐ฐ๐ท", continent: "Asia" },
{ value: "GB", label: "United Kingdom", flag: "๐ฌ๐ง", continent: "Europe" },
{ value: "FR", label: "France", flag: "๐ซ๐ท", continent: "Europe" },
{ value: "DE", label: "Germany", flag: "๐ฉ๐ช", continent: "Europe" },
{ value: "IT", label: "Italy", flag: "๐ฎ๐น", continent: "Europe" },
{ value: "ES", label: "Spain", flag: "๐ช๐ธ", continent: "Europe" },
{ value: "AU", label: "Australia", flag: "๐ฆ๐บ", continent: "Oceania" },
{ value: "NZ", label: "New Zealand", flag: "๐ณ๐ฟ", continent: "Oceania" },
{ value: "FJ", label: "Fiji", flag: "๐ซ๐ฏ", continent: "Oceania" },
],
itemToString: (item) => `${item.flag} ${item.label}`,
itemToValue: (item) => item.value,
})
const continents = Object.entries(
groupBy(countries.items, (item) => item.continent),
)
Icon Button
Here's an example of how to trigger the select component with an IconButton
.
"use client"
import {
HStack,
IconButton,
Portal,
Select,
createListCollection,
useSelectContext,
} from "@chakra-ui/react"
import {
RiAngularjsLine,
RiForbidLine,
RiReactjsLine,
RiSvelteLine,
RiVuejsLine,
} from "react-icons/ri"
const SelectTrigger = () => {
const select = useSelectContext()
const items = select.selectedItems as Framework[]
return (
<IconButton
px="2"
variant="outline"
size="sm"
{...select.getTriggerProps()}
>
{select.hasSelectedItems ? items[0].icon : <RiForbidLine />}
</IconButton>
)
}
const Demo = () => {
return (
<Select.Root
positioning={{ sameWidth: false }}
collection={frameworks}
size="sm"
width="320px"
defaultValue={["react"]}
>
<Select.HiddenSelect />
<Select.Control>
<SelectTrigger />
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content minW="32">
{frameworks.items.map((framework) => (
<Select.Item item={framework} key={framework.value}>
<HStack>
{framework.icon}
{framework.label}
</HStack>
<Select.ItemIndicator />
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)
}
const frameworks = createListCollection({
items: [
{ label: "React.js", value: "react", icon: <RiReactjsLine /> },
{ label: "Vue.js", value: "vue", icon: <RiVuejsLine /> },
{ label: "Angular", value: "angular", icon: <RiAngularjsLine /> },
{ label: "Svelte", value: "svelte", icon: <RiSvelteLine /> },
],
})
interface Framework {
label: string
value: string
icon: React.ReactNode
}
Props
Root
Prop | Default | Type |
---|---|---|
collection * | ListCollection<T> The collection of items | |
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 |
skipAnimationOnMount | false | boolean Whether to allow the initial presence animation. |
unmountOnExit | false | boolean Whether to unmount on exit. |
colorPalette | 'gray' | 'gray' | 'red' | 'orange' | 'yellow' | 'green' | 'teal' | 'blue' | 'cyan' | 'purple' | 'pink' The color palette of the component |
variant | 'outline' | 'outline' | 'subtle' The variant of the component |
size | 'md' | 'xs' | 'sm' | 'md' | 'lg' The size of the component |
as | React.ElementType The underlying element to render. | |
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
unstyled | boolean Whether to remove the component's style. | |
defaultHighlightedValue | string The initial value of the highlighted item when opened. Use when you don't need to control the highlighted value of the select. | |
defaultOpen | boolean Whether the select's open state is controlled by the user | |
defaultValue | string[] The initial default value of the select when rendered. Use when you don't need to control the value of the select. | |
deselectable | boolean Whether the value can be cleared by clicking the selected item. **Note:** this is only applicable for single selection | |
disabled | boolean Whether the select is disabled | |
form | string The associate form of the underlying select. | |
highlightedValue | string The controlled 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 | |
multiple | boolean Whether to allow multiple selection | |
name | string The `name` attribute of the underlying select. | |
onExitComplete | VoidFunction 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 | |
onSelect | (details: SelectionDetails) => void Function called when an item is selected | |
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 controlled keys of the selected items |
Explorer
Explore the Select
component parts interactively. Click on parts in the
sidebar to highlight them in the preview.
Component Anatomy
Hover to highlight, click to select parts
label
positioner
trigger
indicator
clearTrigger
item
itemText
itemIndicator
itemGroup
itemGroupLabel
list
content
root
control
valueText
indicatorGroup
select.recipe.ts