Get Started
Components
- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Avatar
- Badge
- Breadcrumb
- Button
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Collapsible
- Combobox
- Command
- Context Menu
- Data Table
- Date Picker
- Dialog
- Drawer
- Dropdown Menu
- React Hook Form
- Hover Card
- Input
- Input OTP
- Label
- Menubar
- Navigation Menu
- Pagination
- Popover
- Progress
- Radio Group
- Resizable
- Scroll-area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Switch
- Table
- Tabs
- Textarea
- Toast
- Toggle
- Toggle Group
- Tooltip
- Typography
Installation
"use client" import * as React from "react" import { Check, ChevronsUpDown } from "lucide-react" import { cn } from "@/lib/utils" import { Button } from "@/components/ui/button" import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@/components/ui/command" import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover" const frameworks = [ { value: "next.js", label: "Next.js", }, { value: "sveltekit", label: "SvelteKit", }, { value: "nuxt.js", label: "Nuxt.js", }, { value: "remix", label: "Remix", }, { value: "astro", label: "Astro", }, ] export function ComboboxDemo() { const [open, setOpen] = React.useState(false) const [value, setValue] = React.useState("") return ( <Popover open={open} onOpenChange={setOpen}> <PopoverTrigger asChild> <Button variant="outline" role="combobox" aria-expanded={open} className="w-[200px] justify-between" > {value ? frameworks.find((framework) => framework.value === value)?.label : "Select framework..."} <ChevronsUpDown className="opacity-50" /> </Button> </PopoverTrigger> <PopoverContent className="w-[200px] p-0"> <Command> <CommandInput placeholder="Search framework..." className="h-9" /> <CommandList> <CommandEmpty>No framework found.</CommandEmpty> <CommandGroup> {frameworks.map((framework) => ( <CommandItem key={framework.value} value={framework.value} onSelect={(currentValue) => { setValue(currentValue === value ? "" : currentValue) setOpen(false) }} > {framework.label} <Check className={cn( "ml-auto", value === framework.value ? "opacity-100" : "opacity-0" )} /> </CommandItem> ))} </CommandGroup> </CommandList> </Command> </PopoverContent> </Popover> ) }
Installation
The Combobox is built using a composition of the <Popover />
and the <Command />
components.
See installation instructions for the Popover and the Command components.
Usage
"use client" import * as React from "react" import { CheckIcon, ChevronsUpDownIcon } from "lucide-react" import { cn } from "@/lib/utils" import { Button } from "@/components/ui/button" import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@/components/ui/command" import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover" const frameworks = [ { value: "next.js", label: "Next.js", }, { value: "sveltekit", label: "SvelteKit", }, { value: "nuxt.js", label: "Nuxt.js", }, { value: "remix", label: "Remix", }, { value: "astro", label: "Astro", }, ] export function ExampleCombobox() { const [open, setOpen] = React.useState(false) const [value, setValue] = React.useState("") return ( <Popover open={open} onOpenChange={setOpen}> <PopoverTrigger asChild> <Button variant="outline" role="combobox" aria-expanded={open} className="w-[200px] justify-between" > {value ? frameworks.find((framework) => framework.value === value)?.label : "Select framework..."} <ChevronsUpDownIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" /> </Button> </PopoverTrigger> <PopoverContent className="w-[200px] p-0"> <Command> <CommandInput placeholder="Search framework..." /> <CommandList> <CommandEmpty>No framework found.</CommandEmpty> <CommandGroup> {frameworks.map((framework) => ( <CommandItem key={framework.value} value={framework.value} onSelect={(currentValue) => { setValue(currentValue === value ? "" : currentValue) setOpen(false) }} > <CheckIcon className={cn( "mr-2 h-4 w-4", value === framework.value ? "opacity-100" : "opacity-0" )} /> {framework.label} </CommandItem> ))} </CommandGroup> </CommandList> </Command> </PopoverContent> </Popover> ) }
Examples
Combobox
"use client" import * as React from "react" import { Check, ChevronsUpDown } from "lucide-react" import { cn } from "@/lib/utils" import { Button } from "@/components/ui/button" import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@/components/ui/command" import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover" const frameworks = [ { value: "next.js", label: "Next.js", }, { value: "sveltekit", label: "SvelteKit", }, { value: "nuxt.js", label: "Nuxt.js", }, { value: "remix", label: "Remix", }, { value: "astro", label: "Astro", }, ] export function ComboboxDemo() { const [open, setOpen] = React.useState(false) const [value, setValue] = React.useState("") return ( <Popover open={open} onOpenChange={setOpen}> <PopoverTrigger asChild> <Button variant="outline" role="combobox" aria-expanded={open} className="w-[200px] justify-between" > {value ? frameworks.find((framework) => framework.value === value)?.label : "Select framework..."} <ChevronsUpDown className="opacity-50" /> </Button> </PopoverTrigger> <PopoverContent className="w-[200px] p-0"> <Command> <CommandInput placeholder="Search framework..." className="h-9" /> <CommandList> <CommandEmpty>No framework found.</CommandEmpty> <CommandGroup> {frameworks.map((framework) => ( <CommandItem key={framework.value} value={framework.value} onSelect={(currentValue) => { setValue(currentValue === value ? "" : currentValue) setOpen(false) }} > {framework.label} <Check className={cn( "ml-auto", value === framework.value ? "opacity-100" : "opacity-0" )} /> </CommandItem> ))} </CommandGroup> </CommandList> </Command> </PopoverContent> </Popover> ) }
Popover
Status
"use client" import * as React from "react" import { Button } from "@/components/ui/button" import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@/components/ui/command" import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover" type Status = { value: string label: string } const statuses: Status[] = [ { value: "backlog", label: "Backlog", }, { value: "todo", label: "Todo", }, { value: "in progress", label: "In Progress", }, { value: "done", label: "Done", }, { value: "canceled", label: "Canceled", }, ] export function ComboboxPopover() { const [open, setOpen] = React.useState(false) const [selectedStatus, setSelectedStatus] = React.useState<Status | null>( null ) return ( <div className="flex items-center space-x-4"> <p className="text-muted-foreground text-sm">Status</p> <Popover open={open} onOpenChange={setOpen}> <PopoverTrigger asChild> <Button variant="outline" className="w-[150px] justify-start"> {selectedStatus ? <>{selectedStatus.label}</> : <>+ Set status</>} </Button> </PopoverTrigger> <PopoverContent className="p-0" side="right" align="start"> <Command> <CommandInput placeholder="Change status..." /> <CommandList> <CommandEmpty>No results found.</CommandEmpty> <CommandGroup> {statuses.map((status) => ( <CommandItem key={status.value} value={status.value} onSelect={(value) => { setSelectedStatus( statuses.find((priority) => priority.value === value) || null ) setOpen(false) }} > {status.label} </CommandItem> ))} </CommandGroup> </CommandList> </Command> </PopoverContent> </Popover> </div> ) }
Dropdown menu
featureCreate a new project
"use client" import * as React from "react" import { MoreHorizontal } from "lucide-react" import { Button } from "@/components/ui/button" import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@/components/ui/command" import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" const labels = [ "feature", "bug", "enhancement", "documentation", "design", "question", "maintenance", ] export function ComboboxDropdownMenu() { const [label, setLabel] = React.useState("feature") const [open, setOpen] = React.useState(false) return ( <div className="flex w-full flex-col items-start justify-between rounded-md border px-4 py-3 sm:flex-row sm:items-center"> <p className="text-sm leading-none font-medium"> <span className="bg-primary text-primary-foreground mr-2 rounded-lg px-2 py-1 text-xs"> {label} </span> <span className="text-muted-foreground">Create a new project</span> </p> <DropdownMenu open={open} onOpenChange={setOpen}> <DropdownMenuTrigger asChild> <Button variant="ghost" size="sm"> <MoreHorizontal /> </Button> </DropdownMenuTrigger> <DropdownMenuContent align="end" className="w-[200px]"> <DropdownMenuLabel>Actions</DropdownMenuLabel> <DropdownMenuGroup> <DropdownMenuItem>Assign to...</DropdownMenuItem> <DropdownMenuItem>Set due date...</DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuSub> <DropdownMenuSubTrigger>Apply label</DropdownMenuSubTrigger> <DropdownMenuSubContent className="p-0"> <Command> <CommandInput placeholder="Filter label..." autoFocus={true} className="h-9" /> <CommandList> <CommandEmpty>No label found.</CommandEmpty> <CommandGroup> {labels.map((label) => ( <CommandItem key={label} value={label} onSelect={(value) => { setLabel(value) setOpen(false) }} > {label} </CommandItem> ))} </CommandGroup> </CommandList> </Command> </DropdownMenuSubContent> </DropdownMenuSub> <DropdownMenuSeparator /> <DropdownMenuItem className="text-red-600"> Delete <DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut> </DropdownMenuItem> </DropdownMenuGroup> </DropdownMenuContent> </DropdownMenu> </div> ) }
Responsive
You can create a responsive combobox by using the <Popover />
on desktop and the <Drawer />
components on mobile.
"use client" import * as React from "react" import { useMediaQuery } from "@/hooks/use-media-query" import { Button } from "@/components/ui/button" import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@/components/ui/command" import { Drawer, DrawerContent, DrawerTrigger, } from "@/components/ui/drawer" import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover" type Status = { value: string label: string } const statuses: Status[] = [ { value: "backlog", label: "Backlog", }, { value: "todo", label: "Todo", }, { value: "in progress", label: "In Progress", }, { value: "done", label: "Done", }, { value: "canceled", label: "Canceled", }, ] export function ComboBoxResponsive() { const [open, setOpen] = React.useState(false) const isDesktop = useMediaQuery("(min-width: 768px)") const [selectedStatus, setSelectedStatus] = React.useState<Status | null>( null ) if (isDesktop) { return ( <Popover open={open} onOpenChange={setOpen}> <PopoverTrigger asChild> <Button variant="outline" className="w-[150px] justify-start"> {selectedStatus ? <>{selectedStatus.label}</> : <>+ Set status</>} </Button> </PopoverTrigger> <PopoverContent className="w-[200px] p-0" align="start"> <StatusList setOpen={setOpen} setSelectedStatus={setSelectedStatus} /> </PopoverContent> </Popover> ) } return ( <Drawer open={open} onOpenChange={setOpen}> <DrawerTrigger asChild> <Button variant="outline" className="w-[150px] justify-start"> {selectedStatus ? <>{selectedStatus.label}</> : <>+ Set status</>} </Button> </DrawerTrigger> <DrawerContent> <div className="mt-4 border-t"> <StatusList setOpen={setOpen} setSelectedStatus={setSelectedStatus} /> </div> </DrawerContent> </Drawer> ) } function StatusList({ setOpen, setSelectedStatus, }: { setOpen: (open: boolean) => void setSelectedStatus: (status: Status | null) => void }) { return ( <Command> <CommandInput placeholder="Filter status..." /> <CommandList> <CommandEmpty>No results found.</CommandEmpty> <CommandGroup> {statuses.map((status) => ( <CommandItem key={status.value} value={status.value} onSelect={(value) => { setSelectedStatus( statuses.find((priority) => priority.value === value) || null ) setOpen(false) }} > {status.label} </CommandItem> ))} </CommandGroup> </CommandList> </Command> ) }
Form
"use client" import { zodResolver } from "@hookform/resolvers/zod" import { Check, ChevronsUpDown } from "lucide-react" import { useForm } from "react-hook-form" import { toast } from "sonner" import { z } from "zod" import { cn } from "@/lib/utils" import { Button } from "@/components/ui/button" import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@/components/ui/command" import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form" import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover" const languages = [ { label: "English", value: "en" }, { label: "French", value: "fr" }, { label: "German", value: "de" }, { label: "Spanish", value: "es" }, { label: "Portuguese", value: "pt" }, { label: "Russian", value: "ru" }, { label: "Japanese", value: "ja" }, { label: "Korean", value: "ko" }, { label: "Chinese", value: "zh" }, ] as const const FormSchema = z.object({ language: z.string({ required_error: "Please select a language.", }), }) export function ComboboxForm() { const form = useForm<z.infer<typeof FormSchema>>({ resolver: zodResolver(FormSchema), }) function onSubmit(data: z.infer<typeof FormSchema>) { toast("You submitted the following values", { description: ( <pre className="mt-2 w-[320px] rounded-md bg-neutral-950 p-4"> <code className="text-white">{JSON.stringify(data, null, 2)}</code> </pre> ), }) } return ( <Form {...form}> <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6"> <FormField control={form.control} name="language" render={({ field }) => ( <FormItem className="flex flex-col"> <FormLabel>Language</FormLabel> <Popover> <PopoverTrigger asChild> <FormControl> <Button variant="outline" role="combobox" className={cn( "w-[200px] justify-between", !field.value && "text-muted-foreground" )} > {field.value ? languages.find( (language) => language.value === field.value )?.label : "Select language"} <ChevronsUpDown className="opacity-50" /> </Button> </FormControl> </PopoverTrigger> <PopoverContent className="w-[200px] p-0"> <Command> <CommandInput placeholder="Search framework..." className="h-9" /> <CommandList> <CommandEmpty>No framework found.</CommandEmpty> <CommandGroup> {languages.map((language) => ( <CommandItem value={language.label} key={language.value} onSelect={() => { form.setValue("language", language.value) }} > {language.label} <Check className={cn( "ml-auto", language.value === field.value ? "opacity-100" : "opacity-0" )} /> </CommandItem> ))} </CommandGroup> </CommandList> </Command> </PopoverContent> </Popover> <FormDescription> This is the language that will be used in the dashboard. </FormDescription> <FormMessage /> </FormItem> )} /> <Button type="submit">Submit</Button> </form> </Form> ) }
Deploy your shadcn/ui app on Vercel
Trusted by OpenAI, Sonos, Adobe, and more.
Vercel provides tools and infrastructure to deploy apps and features at scale.
Deploy to Vercel