"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
Here's an example of how to use the Select
within a Dialog
component.
Due to the focus trap within the dialog, it's important to change the portal target from the document's body to the dialog's content.
"use client"
import {
Button,
CloseButton,
Dialog,
Portal,
Select,
createListCollection,
} from "@chakra-ui/react"
import { useRef } from "react"
const Demo = () => {
const contentRef = useRef<HTMLDivElement>(null)
return (
<Dialog.Root>
<Dialog.Trigger asChild>
<Button variant="outline">Open Dialog</Button>
</Dialog.Trigger>
<Portal>
<Dialog.Backdrop />
<Dialog.Positioner>
<Dialog.Content ref={contentRef}>
<Dialog.CloseTrigger asChild>
<CloseButton />
</Dialog.CloseTrigger>
<Dialog.Header>
<Dialog.Title>Select in Dialog</Dialog.Title>
</Dialog.Header>
<Dialog.Body>
<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>
<Portal container={contentRef}>
<Select.Positioner>
<Select.Content>
{frameworks.items.map((item) => (
<Select.Item item={item} key={item.value}>
{item.label}
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
</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" },
],
})
Alternatively, you can avoid portalling the Select.Positioner
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 |
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 |
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. | |
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 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 | () => 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. |