Combobox

Combobox composes Base UI’s combobox primitives with Density attributes on inputs and items. Its text input opens and filters the option list, while selected options render through ListItem active state instead of visible selected text.

import { useState } from "react";
import { Combobox } from "density-base-ui";

const projects = [
  { value: "Apollo", label: "Apollo" },
  { value: "Beacon", label: "Beacon" },
  { value: "Nimbus", label: "Nimbus" },
];

export default function ComboboxExample() {
  const [project, setProject] = useState(projects[1]);

  return (
    <Combobox
      items={projects}
      value={project}
      onValueChange={(nextProject) => {
        setProject(nextProject as (typeof projects)[number]);
      }}
      placeholder="Find a project"
    />
  );
}

Label

import { useState } from "react";
import { Combobox } from "density-base-ui";

const items = [
  { value: "one", label: "One" },
  { value: "two", label: "Two" },
];

export default function ComboboxLabelExample() {
  const [selectedItem, setSelectedItem] = useState(items[0]);

  return (
    <Combobox
      value={selectedItem}
      onValueChange={(item) => setSelectedItem(item as (typeof items)[number])}
      items={items}
      label="Field label"
      placeholder="Search"
    />
  );
}

Hint

Choose one
import { useState } from "react";
import { Combobox } from "density-base-ui";

const items = [
  { value: "one", label: "One" },
  { value: "two", label: "Two" },
];

export default function ComboboxHintExample() {
  const [selectedItem, setSelectedItem] = useState(items[0]);

  return (
    <Combobox
      value={selectedItem}
      onValueChange={(item) => setSelectedItem(item as (typeof items)[number])}
      items={items}
      label="Field label"
      hint="Choose one"
      placeholder="Search"
    />
  );
}

Error

Choose a project owner.
import { useState } from "react";
import { Combobox } from "density-base-ui";

const items = [
  { value: "one", label: "One" },
  { value: "two", label: "Two" },
];

export default function ComboboxErrorExample() {
  const [selectedItem, setSelectedItem] = useState(items[0]);

  return (
    <Combobox
      value={selectedItem}
      onValueChange={(item) => setSelectedItem(item as (typeof items)[number])}
      errorMessage="Choose a project owner."
      hint="Choose one."
      items={items}
      label="Project owner"
      placeholder="Search"
    />
  );
}

Field orientation

The field copy stacks above the input.
The field copy sits beside the input.
import { useState } from "react";
import { Combobox, Separator, View } from "density-base-ui";

const items = [
  { value: "one", label: "One" },
  { value: "two", label: "Two" },
];

export default function ComboboxFieldOrientationExample() {
  const [verticalItem, setVerticalItem] = useState(items[0]);
  const [horizontalItem, setHorizontalItem] = useState(items[0]);

  return (
    <View align="start" gap="md" padding={false}>
      <Combobox
        value={verticalItem}
        onValueChange={(item) =>
          setVerticalItem(item as (typeof items)[number])
        }
        items={items}
        label="Vertical field"
        hint="The field copy stacks above the input."
        fieldOrientation="vertical"
        placeholder="Search"
      />
      <Separator />
      <Combobox
        value={horizontalItem}
        onValueChange={(item) =>
          setHorizontalItem(item as (typeof items)[number])
        }
        items={items}
        label="Horizontal field"
        hint="The field copy sits beside the input."
        fieldOrientation="horizontal"
        placeholder="Search"
      />
    </View>
  );
}

Placeholder

import { useState } from "react";
import { Combobox } from "density-base-ui";

const items = [
  { value: "one", label: "One" },
  { value: "two", label: "Two" },
];

export default function ComboboxPlaceholderExample() {
  const [selectedItem, setSelectedItem] = useState(items[0]);

  return (
    <Combobox
      value={selectedItem}
      onValueChange={(item) => setSelectedItem(item as (typeof items)[number])}
      items={items}
      placeholder="Search"
    />
  );
}

Empty label

import { useState } from "react";
import { Combobox } from "density-base-ui";

const items = [
  { value: "one", label: "One" },
  { value: "two", label: "Two" },
];

export default function ComboboxEmptyLabelExample() {
  const [selectedItem, setSelectedItem] = useState(items[0]);

  return (
    <Combobox
      value={selectedItem}
      onValueChange={(item) => setSelectedItem(item as (typeof items)[number])}
      items={items}
      emptyLabel="Nothing found"
      placeholder="Search"
    />
  );
}

Input emphasis

import { useState } from "react";
import { Combobox, View } from "density-base-ui";

const items = [
  { value: "one", label: "One" },
  { value: "two", label: "Two" },
];

export default function ComboboxEmphasisExample() {
  const [selectedItems, setSelectedItems] = useState<
    Record<string, (typeof items)[number]>
  >({});
  const updateSelectedItem = (emphasis: string, item: unknown) => {
    setSelectedItems((currentItems) => ({
      ...currentItems,
      [emphasis]: item as (typeof items)[number],
    }));
  };

  return (
    <View align="start" gap="md" padding={false}>
      <Combobox
        value={selectedItems.primary ?? items[0]}
        onValueChange={(item) => updateSelectedItem("primary", item)}
        items={items}
        emphasis="primary"
        placeholder="Search"
      />
      <Combobox
        value={selectedItems.secondary ?? items[0]}
        onValueChange={(item) => updateSelectedItem("secondary", item)}
        items={items}
        emphasis="secondary"
        placeholder="Search"
      />
      <Combobox
        value={selectedItems.outline ?? items[0]}
        onValueChange={(item) => updateSelectedItem("outline", item)}
        items={items}
        emphasis="outline"
        placeholder="Search"
      />
      <Combobox
        value={selectedItems.ghostPrimary ?? items[0]}
        onValueChange={(item) => updateSelectedItem("ghostPrimary", item)}
        items={items}
        emphasis="ghost-primary"
        placeholder="Search"
      />
      <Combobox
        value={selectedItems.ghostSecondary ?? items[0]}
        onValueChange={(item) => updateSelectedItem("ghostSecondary", item)}
        items={items}
        emphasis="ghost-secondary"
        placeholder="Search"
      />
      <Combobox
        value={selectedItems.ghostTertiary ?? items[0]}
        onValueChange={(item) => updateSelectedItem("ghostTertiary", item)}
        items={items}
        emphasis="ghost-tertiary"
        placeholder="Search"
      />
    </View>
  );
}

Size

import { useState } from "react";
import { Combobox, View } from "density-base-ui";

const items = [
  { value: "one", label: "One" },
  { value: "two", label: "Two" },
];

export default function ComboboxSizeExample() {
  const [selectedItems, setSelectedItems] = useState<
    Record<string, (typeof items)[number]>
  >({});
  const updateSelectedItem = (size: string, item: unknown) => {
    setSelectedItems((currentItems) => ({
      ...currentItems,
      [size]: item as (typeof items)[number],
    }));
  };

  return (
    <View align="start" gap="md" padding={false}>
      <Combobox
        value={selectedItems["2xs"] ?? items[0]}
        onValueChange={(item) => updateSelectedItem("2xs", item)}
        items={items}
        size="2xs"
        placeholder="Search"
      />
      <Combobox
        value={selectedItems.xs ?? items[0]}
        onValueChange={(item) => updateSelectedItem("xs", item)}
        items={items}
        size="xs"
        placeholder="Search"
      />
      <Combobox
        value={selectedItems.sm ?? items[0]}
        onValueChange={(item) => updateSelectedItem("sm", item)}
        items={items}
        size="sm"
        placeholder="Search"
      />
      <Combobox
        value={selectedItems.md ?? items[0]}
        onValueChange={(item) => updateSelectedItem("md", item)}
        items={items}
        size="md"
        placeholder="Search"
      />
      <Combobox
        value={selectedItems.lg ?? items[0]}
        onValueChange={(item) => updateSelectedItem("lg", item)}
        items={items}
        size="lg"
        placeholder="Search"
      />
      <Combobox
        value={selectedItems.xl ?? items[0]}
        onValueChange={(item) => updateSelectedItem("xl", item)}
        items={items}
        size="xl"
        placeholder="Search"
      />
      <Combobox
        value={selectedItems["2xl"] ?? items[0]}
        onValueChange={(item) => updateSelectedItem("2xl", item)}
        items={items}
        size="2xl"
        placeholder="Search"
      />
      <Combobox
        value={selectedItems["3xl"] ?? items[0]}
        onValueChange={(item) => updateSelectedItem("3xl", item)}
        items={items}
        size="3xl"
        placeholder="Search"
      />
    </View>
  );
}

Padding

Combobox adds padding around its popup list by default. Set padding={false} when the list should reach the popup edges. This does not affect the input.

import { useState } from "react";
import { Combobox, View } from "density-base-ui";

const items = [
  { value: "one", label: "One" },
  { value: "two", label: "Two" },
];

export default function ComboboxPaddingExample() {
  const [selectedItems, setSelectedItems] = useState<
    Record<string, (typeof items)[number]>
  >({});
  const updateSelectedItem = (paddingState: string, item: unknown) => {
    setSelectedItems((currentItems) => ({
      ...currentItems,
      [paddingState]: item as (typeof items)[number],
    }));
  };

  return (
    <View align="start" gap="md" padding={false}>
      <Combobox
        value={selectedItems.unpadded ?? items[0]}
        onValueChange={(item) => updateSelectedItem("unpadded", item)}
        items={items}
        padding={false}
        placeholder="Search"
      />
      <Combobox
        value={selectedItems.padded ?? items[0]}
        onValueChange={(item) => updateSelectedItem("padded", item)}
        items={items}
        placeholder="Search"
      />
    </View>
  );
}