"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 { standardSchemaResolver } from "@hookform/resolvers/standard-schema"
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: standardSchemaResolver(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
fixedto avoid the select from being clipped by the dialog. - Set
hideWhenDetachedtotrueto 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 | booleanWhether the select should close after an item is selected |
composite | true | booleanWhether the select is a composed with other composite widgets like tabs or combobox |
lazyMount | false | booleanWhether to enable lazy mounting |
loopFocus | false | booleanWhether to loop the keyboard navigation through the options |
skipAnimationOnMount | false | booleanWhether to allow the initial presence animation. |
unmountOnExit | false | booleanWhether 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.ElementTypeThe underlying element to render. | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
unstyled | booleanWhether to remove the component's style. | |
defaultHighlightedValue | stringThe initial value of the highlighted item when opened. Use when you don't need to control the highlighted value of the select. | |
defaultOpen | booleanWhether 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 | booleanWhether the value can be cleared by clicking the selected item. **Note:** this is only applicable for single selection | |
disabled | booleanWhether the select is disabled | |
form | stringThe associate form of the underlying select. | |
highlightedValue | stringThe controlled key of the highlighted item | |
id | stringThe 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 | booleanWhether to synchronize the present change immediately or defer it to the next frame | |
invalid | booleanWhether the select is invalid | |
multiple | booleanWhether to allow multiple selection | |
name | stringThe `name` attribute of the underlying select. | |
onExitComplete | VoidFunctionFunction called when the animation ends in the closed state | |
onFocusOutside | (event: FocusOutsideEvent) => voidFunction called when the focus is moved outside the component | |
onHighlightChange | (details: HighlightChangeDetails<T>) => voidThe callback fired when the highlighted item changes. | |
onInteractOutside | (event: InteractOutsideEvent) => voidFunction called when an interaction happens outside the component | |
onOpenChange | (details: OpenChangeDetails) => voidFunction called when the popup is opened | |
onPointerDownOutside | (event: PointerDownOutsideEvent) => voidFunction called when the pointer is pressed down outside the component | |
onSelect | (details: SelectionDetails) => voidFunction called when an item is selected | |
onValueChange | (details: ValueChangeDetails<T>) => voidThe callback fired when the selected item changes. | |
open | booleanWhether the select menu is open | |
positioning | PositioningOptionsThe positioning options of the menu. | |
present | booleanWhether the node is present (controlled by the user) | |
readOnly | booleanWhether the select is read-only | |
required | booleanWhether the select is required | |
scrollToIndexFn | (details: ScrollToIndexDetails) => voidFunction 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