Combobox

A searchable dropdown component that combines a popover with a command menu for filtering and selection.

User Search

Search and select a user from the list

"use client";

import { useState } from "react";
import { Combobox, ComboboxOption } from "@/components/ui/combobox/combobox";
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar/avatar";
import styles from "./combobox-demo.module.css";

interface User extends ComboboxOption {
  email: string;
  avatar: string;
}

const users = [
  {
    value: "preetecool",
    label: "preetecool",
    email: "@preetecool",
    avatar: "https://github.com/preetecool.png",
  },
  {
    value: "john-doe",
    label: "John Doe",
    email: "john@example.com",
    avatar: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=32&h=32&fit=crop&crop=face",
  },
  {
    value: "jane-smith",
    label: "Jane Smith",
    email: "jane@example.com",
    avatar: "https://images.unsplash.com/photo-1494790108755-2616b612b786?w=32&h=32&fit=crop&crop=face",
  },
  {
    value: "mike-johnson",
    label: "Mike Johnson",
    email: "mike@example.com",
    avatar: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=32&h=32&fit=crop&crop=face",
  },
  {
    value: "sarah-wilson",
    label: "Sarah Wilson",
    email: "sarah@example.com",
    avatar: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=32&h=32&fit=crop&crop=face",
  },
  {
    value: "alex-brown",
    label: "Alex Brown",
    email: "alex@example.com",
    avatar: "https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=32&h=32&fit=crop&crop=face",
  },
  {
    value: "emma-davis",
    label: "Emma Davis",
    email: "emma@example.com",
    avatar: "https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=32&h=32&fit=crop&crop=face",
  },
];

export default function ComboboxDemo() {
  const [selectedUser, setSelectedUser] = useState<string>("");

  const renderTrigger = (selectedOption: ComboboxOption | undefined) => {
    if (!selectedOption) return null;

    const user = selectedOption as User;

    return (
      <div className={styles.userContainer}>
        <Avatar className={styles.avatar}>
          <AvatarImage src={user.avatar} alt={user.label} />
          <AvatarFallback>
            {user.label
              .split(" ")
              .map((n: string) => n[0])
              .join("")}
          </AvatarFallback>
        </Avatar>
        <div className={styles.userInfo}>
          <div className={styles.userName}>{user.label}</div>
          <div className={styles.userEmail}>{user.email}</div>
        </div>
      </div>
    );
  };

  const renderOption = (option: ComboboxOption) => {
    const user = option as User;

    return (
      <div className={styles.userContainer}>
        <Avatar className={styles.avatar}>
          <AvatarImage src={user.avatar} alt={user.label} />
          <AvatarFallback>
            {user.label
              .split(" ")
              .map((n: string) => n[0])
              .join("")}
          </AvatarFallback>
        </Avatar>
        <div className={styles.userInfoOption}>
          <div className={styles.userName}>{user.label}</div>
          <div className={styles.userEmail}>{user.email}</div>
        </div>
      </div>
    );
  };

  return (
    <div className={styles.container}>
      <div className={styles.header}>
        <h3 className={styles.title}>User Search</h3>
        <p className={styles.description}>Search and select a user from the list</p>
      </div>

      <div className={styles.comboboxWrapper}>
        <Combobox
          options={users}
          value={selectedUser}
          onValueChange={setSelectedUser}
          placeholder="Select a user..."
          searchPlaceholder="Search users..."
          emptyText="No user found."
          renderTrigger={renderTrigger}
          renderOption={renderOption}
          contentWidth="300px"
        />
      </div>
    </div>
  );
}

Installation

npx shadcn@latest add https://roiui.com/r/combobox.json

Anatomy

anatomy
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from '../ui/command'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '../ui/popover'
import { Button } from '../ui/button'

<Popover>
<PopoverTrigger render={<Button />}>

</PopoverTrigger>
<PopoverContent>
  <Command>
    <CommandInput  />
    <CommandList>
      <CommandEmpty></CommandEmpty>
      <CommandGroup>
        <CommandItem></CommandItem>
        <CommandItem></CommandItem>
      </CommandGroup>
    </CommandList>
  </Command>
</PopoverContent>
</Popover>

Props

PropTypeDefault
options
Array<{value: string, label: string}>-
value
string-
onValueChange
(value: string) => void-
placeholder
string-
searchPlaceholder
string-
emptyText
string-
disabled
booleanfalse
className
string-