shadcn.io is not affiliated with official shadcn/ui
Shadcn Listbox for React and Tailwind
Select a single or multiple items from a list.
Brazil
Mexico
Ireland
Installation
bunx --bun shadcn@latest add https://kit.dev/r/listbox.jsonpnpm dlx shadcn@latest add https://kit.dev/r/listbox.jsonnpx shadcn@latest add https://kit.dev/r/listbox.jsonyarn shadcn@latest add https://kit.dev/r/listbox.jsonThis component depends on Menu. Install it first if you haven't already.
Install the following dependencies:
bun add @ark-ui/react lucide-react tailwind-variantspnpm add @ark-ui/react lucide-react tailwind-variantsnpm install @ark-ui/react lucide-react tailwind-variantsyarn add @ark-ui/react lucide-react tailwind-variantsCopy and paste the following code into your project.
"use client";
import {
Listbox as ArkListbox,
useListboxContext,
} from "@ark-ui/react/listbox";
import { CheckIcon } from "lucide-react";
import type React from "react";
import { tv, type VariantProps } from "tailwind-variants";
import { cn } from "@/lib/utils";
import { MenuShortcut } from "@/components/ui/menu";
export const useListbox = useListboxContext;
export const Listbox: ArkListbox.RootComponent = (props) => {
const { className, ...rest } = props;
return (
<ArkListbox.Root
className={cn(
"w-full",
"flex flex-col gap-1.5",
"text-foreground",
className
)}
data-slot="listbox"
{...rest}
/>
);
};
export const ListboxContent = (
props: React.ComponentProps<typeof ArkListbox.Content>
) => {
const { className, ...rest } = props;
return (
<ArkListbox.Content
className={cn(
"w-full",
"flex flex-col gap-1",
"outline-hidden",
"overflow-hidden",
"data-[orientation=horizontal]:max-h-none data-[orientation=horizontal]:flex-row",
className
)}
data-slot="listbox-content"
{...rest}
/>
);
};
const listboxItemVariants = tv({
base: [
"group/listbox-item",
"relative",
"flex items-center gap-2",
"px-2.5 py-2",
"rounded-xl",
"select-none text-sm",
"cursor-pointer",
"outline-hidden",
"data-disabled:pointer-events-none data-disabled:opacity-64",
"[&_svg:not([class*='size-'])]:size-3.5 [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0",
],
variants: {
variant: {
default: [
"text-popover-foreground",
"data-[state=checked]:bg-accent data-[state=checked]:text-accent-foreground",
"hover:bg-accent hover:text-accent-foreground",
"data-highlighted:bg-accent data-highlighted:text-accent-foreground",
],
destructive: [
"text-destructive dark:text-destructive-foreground",
"hover:bg-destructive/10 dark:hover:bg-destructive-foreground/10",
"data-highlighted:bg-destructive/10 dark:data-highlighted:bg-destructive-foreground/10",
"**:[svg]:text-destructive! dark:**:[svg]:text-destructive-foreground!",
],
},
},
defaultVariants: {
variant: "default",
},
});
interface ListboxItemProps
extends React.ComponentProps<typeof ArkListbox.Item>,
VariantProps<typeof listboxItemVariants> {}
export const ListboxItem = (props: ListboxItemProps) => {
const { variant = "default", className, ...rest } = props;
return (
<ArkListbox.Item
className={cn(listboxItemVariants({ variant }), className)}
data-slot="listbox-item"
data-variant={variant}
{...rest}
/>
);
};
export const ListboxItemText = (
props: React.ComponentProps<typeof ArkListbox.ItemText>
) => {
const { className, ...rest } = props;
return (
<ArkListbox.ItemText
className={cn(
"min-w-0",
"flex-1",
"text-ellipsis whitespace-nowrap",
"overflow-hidden",
className
)}
data-slot="listbox-item-text"
{...rest}
/>
);
};
interface ListboxItemGroupProps
extends React.ComponentProps<typeof ArkListbox.ItemGroup> {
/**
* The heading of the listbox item group.
*/
heading?: string;
}
export const ListboxItemGroup = (props: ListboxItemGroupProps) => {
const { heading, className, children, ...rest } = props;
return (
<ArkListbox.ItemGroup
className={cn("flex flex-col gap-1", className)}
data-slot="listbox-item-group"
{...rest}
>
{!!heading && <ListboxItemGroupLabel>{heading}</ListboxItemGroupLabel>}
{children}
</ArkListbox.ItemGroup>
);
};
export const ListboxItemGroupLabel = (
props: React.ComponentProps<typeof ArkListbox.ItemGroupLabel>
) => {
const { className, ...rest } = props;
return (
<ArkListbox.ItemGroupLabel
className={cn(
"px-2.5 py-2",
"font-medium text-muted-foreground",
"pointer-events-none",
className
)}
data-slot="listbox-item-group-label"
{...rest}
/>
);
};
export const ListboxValueText = (
props: React.ComponentProps<typeof ArkListbox.ValueText>
) => {
const { className, ...rest } = props;
return (
<ArkListbox.ValueText
className={cn("font-normal", className)}
data-slot="listbox-value-text"
{...rest}
/>
);
};
export const ListboxItemIndicator = (
props: React.ComponentProps<typeof ArkListbox.ItemIndicator>
) => {
const { className, children, ...rest } = props;
return (
<ArkListbox.ItemIndicator
className={cn(
"flex shrink-0 items-center justify-center",
"[&_svg]:text-primary!",
"zoom-in-95 fade-in-0 animate-in",
"motion-reduce:animate-none!",
className
)}
data-slot="listbox-item-indicator"
{...rest}
>
{children ?? <CheckIcon />}
</ArkListbox.ItemIndicator>
);
};
export const ListboxEmpty = (
props: React.ComponentProps<typeof ArkListbox.Empty>
) => {
const { className, ...rest } = props;
return (
<ArkListbox.Empty
className={cn(
"px-2 py-1.5",
"text-center text-muted-foreground text-sm",
className
)}
data-slot="listbox-empty"
{...rest}
/>
);
};
export const ListboxShortcut = (
props: React.ComponentProps<typeof MenuShortcut>
) => <MenuShortcut data-slot="listbox-shortcut" {...props} />;Update the import paths to match your project setup.
Anatomy
ListboxRoot
├── ListboxLabel
├── ListboxInput
├── ListboxItem
│ ├── ListboxItemText
│ ├── ListboxItemIndicator
│ └── ListboxItemGroup
│ └── ListboxItemGroupLabel
├── ListboxContent
└── ListboxValueTextUsage
import { createListCollection } from "@ark-ui/react/collection";
import {
Listbox,
ListboxContent,
ListboxItem,
ListboxItemIndicator,
ListboxItemText,
} from "@/components/ui/listbox";const Example = () => (
<Listbox
className="w-full max-w-64"
collection={collection}
defaultValue={["br"]}
>
<ListboxContent>
{collection.items.map((item) => (
<ListboxItem item={item} key={item.value}>
<ListboxItemText>{item.label}</ListboxItemText>
<ListboxItemIndicator />
</ListboxItem>
))}
</ListboxContent>
</Listbox>
);
const collection = createListCollection({
items: [
{ label: "Brazil", value: "br" },
{ label: "Mexico", value: "mx" },
{ label: "Ireland", value: "ie" },
],
});Accessibility
| Key | Description |
|---|---|
ArrowUp | Move up. |
ArrowDown | Move down. |
ArrowLeft | Move left. |
ArrowRight | Move right. |
Home | Go to the start. |
End | Go to the end. |
Enter | Activate the focused item. |
Ctrl+A | Select all. |
Space | Activate the focused item. |
Escape | Clear the selection. |
Demos
Controlled
Disabled
Disabled Item
Grid
Grouping
Horizontal
Image Explorer
Selection Extended
Selection Multiple
Selection None
Transfer List
With Description
With Filter
With Icon
With Popover
API Reference
ListboxItem
div
PropType
AttributeType
ListboxItemText
div
PropType
AttributeType
ListboxItemIndicator
div
PropType
AttributeType
ListboxItemGroup
div
PropType
AttributeType
ListboxItemGroupLabel
div
PropType
AttributeType
ListboxContent
div
PropType
AttributeType
CSS variableType
ListboxValueText
span
PropType
AttributeType
ListboxEmpty
div
PropType
AttributeType
ListboxShortcut
PropType
AttributeType
useListbox
PropType