Skip to main content

Overview

Combobox combines a text input with a dropdown menu, allowing users to search and filter options as they type. Selected items are displayed as dismissible tags, making it ideal for selecting multiple items with search functionality.

Basic Usage

import { Combobox } from 'grauity';
import { useState } from 'react';

function MyForm() {
  const [value, setValue] = useState([]);

  return (
    <Combobox
      name="tags"
      label="Tags"
      placeholder="Type to search..."
      multiple
      items={[
        { type: 'option', label: 'React', value: 'react' },
        { type: 'option', label: 'TypeScript', value: 'typescript' },
        { type: 'option', label: 'JavaScript', value: 'javascript' },
        { type: 'option', label: 'CSS', value: 'css' },
      ]}
      value={value}
      onChange={setValue}
    />
  );
}

Single Selection

import { Combobox } from 'grauity';
import { useState } from 'react';

function SingleSelectCombobox() {
  const [movie, setMovie] = useState(null);

  const movies = [
    { type: 'option', label: 'Avatar', value: 'avatar' },
    { type: 'option', label: 'Avengers: Endgame', value: 'endgame' },
    { type: 'option', label: 'Avatar: The Way of Water', value: 'avatar2' },
    { type: 'option', label: 'Star Wars: The Force Awakens', value: 'starwars' },
  ];

  return (
    <Combobox
      name="movie"
      label="Favorite Movie"
      placeholder="Search movies..."
      multiple={false}
      items={movies}
      value={movie}
      onChange={setMovie}
    />
  );
}

Multiple Selection

import { Combobox } from 'grauity';
import { useState } from 'react';

function MultiSelectCombobox() {
  const [skills, setSkills] = useState([]);

  const skillOptions = [
    { type: 'option', label: 'React', value: 'react' },
    { type: 'option', label: 'Vue', value: 'vue' },
    { type: 'option', label: 'Angular', value: 'angular' },
    { type: 'option', label: 'Svelte', value: 'svelte' },
    { type: 'option', label: 'TypeScript', value: 'typescript' },
    { type: 'option', label: 'JavaScript', value: 'javascript' },
  ];

  return (
    <Combobox
      name="skills"
      label="Technical Skills"
      placeholder="Type to search skills..."
      multiple
      items={skillOptions}
      value={skills}
      onChange={setSkills}
      helpMessage="Select all that apply"
    />
  );
}
By default, Combobox uses client-side filtering. The search matches against the label and description fields.

Custom Search (Server-side)

import { Combobox } from 'grauity';
import { useState, useEffect } from 'react';

function ServerSearchCombobox() {
  const [value, setValue] = useState([]);
  const [options, setOptions] = useState([]);
  const [loading, setLoading] = useState(false);

  const handleTextInputChange = async (searchText) => {
    if (!searchText) {
      setOptions([]);
      return;
    }

    setLoading(true);
    // Simulate API call
    const results = await searchAPI(searchText);
    setOptions(
      results.map(item => ({
        type: 'option',
        label: item.name,
        value: item.id,
      }))
    );
    setLoading(false);
  };

  return (
    <Combobox
      name="search"
      label="Search"
      placeholder="Type to search..."
      multiple
      useDefaultSearchMethod={false}
      items={options}
      value={value}
      onChange={setValue}
      onTextInputChange={handleTextInputChange}
    />
  );
}
Set useDefaultSearchMethod={false} to disable client-side filtering when implementing custom search logic.

Custom Tag Renderer

import { Combobox, Button } from 'grauity';
import { useState } from 'react';

function CustomTagCombobox() {
  const [value, setValue] = useState([]);

  return (
    <Combobox
      name="custom"
      label="Custom Tags"
      placeholder="Type to search..."
      multiple
      items={[
        { type: 'option', label: 'Option 1', value: '1' },
        { type: 'option', label: 'Option 2', value: '2' },
        { type: 'option', label: 'Option 3', value: '3' },
      ]}
      value={value}
      onChange={setValue}
      renderValue={({ item, onDismiss }) => (
        <Button
          color="success"
          size="small"
          icon="close"
          iconPosition="right"
          onClick={onDismiss}
        >
          {item.label}
        </Button>
      )}
    />
  );
}

Dismiss on Backspace

import { Combobox } from 'grauity';
import { useState } from 'react';

function BackspaceDismissCombobox() {
  const [value, setValue] = useState([]);

  return (
    <Combobox
      name="tags"
      label="Tags"
      placeholder="Type to add tags..."
      multiple
      shouldDismissOnBackspace
      items={[
        { type: 'option', label: 'Tag 1', value: '1' },
        { type: 'option', label: 'Tag 2', value: '2' },
        { type: 'option', label: 'Tag 3', value: '3' },
      ]}
      value={value}
      onChange={setValue}
      helpMessage="Press Backspace to remove the last tag"
    />
  );
}

With Sections and Icons

import { Combobox } from 'grauity';
import { useState } from 'react';

function GroupedCombobox() {
  const [value, setValue] = useState([]);

  return (
    <Combobox
      name="movies"
      label="Movies"
      placeholder="Search movies..."
      multiple
      items={[
        { type: 'subheader', title: 'TOP RATED' },
        {
          type: 'option',
          label: 'The Shawshank Redemption',
          value: 'shawshank',
          leftIcon: 'check-circle',
        },
        {
          type: 'option',
          label: 'The Godfather',
          value: 'godfather',
          leftIcon: 'check-circle',
        },
        { type: 'divider' },
        { type: 'subheader', title: 'RECENT' },
        {
          type: 'option',
          label: 'Oppenheimer',
          value: 'oppenheimer',
          leftIcon: 'check-circle',
        },
      ]}
      value={value}
      onChange={setValue}
    />
  );
}

Props

Trigger Props

name
string
required
The name of the combobox field.
label
string
The label displayed above the combobox.
placeholder
string
default:"'Select'"
Placeholder text for the input field.
isRequired
boolean
default:false
Whether the field is required.
isDisabled
boolean
default:false
Whether the combobox is disabled.
helpMessage
string
Helper text displayed below the combobox.
errorMessage
string
Error message displayed below the combobox.
color
string
default:"'brand'"
Color theme. Options: 'brand' | 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info'
onTextInputChange
function
Callback fired when the input text changes.
(text: string) => void
tagProps
object
Props passed to the Tag component for selected items.
renderValue
function
Custom renderer for selected value tags.
(props: {
  index: number;
  item: BaseItemOptionProps;
  onDismiss: () => void;
}) => React.ReactNode
shouldDismissOnBackspace
boolean
default:false
Whether pressing Backspace (when input is empty) should remove the last selected item.
items
array
required
Array of menu items. Same format as Dropdown items.
value
BaseItemOptionProps | BaseItemOptionProps[]
Currently selected value(s).
multiple
boolean
default:false
Whether multiple items can be selected.
useDefaultSearchMethod
boolean
default:true
Whether to use built-in client-side search. Set to false for custom search implementations.
showActionButtons
boolean
default:false
Whether to show Apply and Clear All buttons.
applyOnOptionSelectInMultipleMode
boolean
default:true
Whether onChange is called immediately when selecting options in multiple mode.
onChange
function
Callback fired when selection changes.
(value: BaseItemOptionProps | BaseItemOptionProps[]) => void
onClose
function
Callback fired when the dropdown closes.
(value: BaseItemOptionProps | BaseItemOptionProps[]) => void
menuProps
object
Menu positioning configuration:
  • width: Menu width (default: '300px')
  • fullWidth: Match trigger width (default: true)

TypeScript

import { ComboboxProps, BaseItemOptionProps } from 'grauity';

interface ComboboxProps {
  name: string;
  label?: string;
  placeholder?: string;
  isRequired?: boolean;
  isDisabled?: boolean;
  helpMessage?: string;
  errorMessage?: string;
  color?: string;
  multiple?: boolean;
  items: BaseItemProps[];
  value?: BaseItemOptionProps | BaseItemOptionProps[];
  onChange?: (value: any) => void;
  onClose?: (value: any) => void;
  onTextInputChange?: (text: string) => void;
  useDefaultSearchMethod?: boolean;
  renderValue?: (props: any) => React.ReactNode;
  shouldDismissOnBackspace?: boolean;
  tagProps?: Omit<TagProps, 'children'>;
  menuProps?: {
    width?: string;
    fullWidth?: boolean;
  };
}

Accessibility

  • Keyboard navigation with arrow keys and Enter
  • Screen reader support with proper ARIA attributes
  • Focus management between input and menu
  • Selected items are dismissible via keyboard
  • Support for assistive technologies
When implementing custom search with useDefaultSearchMethod={false}, ensure your search function handles debouncing to avoid excessive API calls.

Best Practices

  1. Use for searchable selections: Ideal when users need to search through many options
  2. Debounce API calls: When using server-side search, debounce the search function
  3. Provide feedback: Show loading states during async operations
  4. Clear empty states: Display helpful messages when no results are found
  5. Tag management: Make it easy to review and remove selected items

Source

View the source code on GitHub:

Build docs developers (and LLMs) love