Klarhimmel UI

Combobox

Stable

A text field that filters a list as you type and picks one option.

npx shadcn add @klarhimmel/combobox @klarhimmel/input

When to use

  • Use for

    Long lists people can name: countries, staff, products. They type, the list narrows.

  • Not for

    Fewer than seven options. Use Select. Free text with suggestions. Use Input.

  • Pairs with

    Field, forms.

Preview

Start typing to narrow the list.

Nothing chosen yet.

combobox.tsx
import { Theme } from '@/components/klarhimmel/theme/style';
import { Combobox } from '@/components/klarhimmel/ui/combobox';
import { Field } from '@/components/klarhimmel/ui/input';

export default function Example() {
  return (
    <Theme theme="brand">
      <Field label="Country">
        {(p) => (
          <Combobox
            {...p}
            options={[
              { value: 'dk', label: 'Denmark' },
              { value: 'fi', label: 'Finland' },
              { value: 'no', label: 'Norway' },
              { value: 'se', label: 'Sweden' },
            ]}
            placeholder="Search countries"
          />
        )}
      </Field>
    </Theme>
  );
}

Install

Import

import { Combobox } from '@/components/klarhimmel/ui/combobox';
Lands in
components/klarhimmel/ui/combobox.tsx, plus cn() and the theme item if they are missing. Import theme.css after Tailwind.
Render in
Render it inside <Theme>, once near the root of your app. Tokens and fonts are scoped to it, so outside it the component is unstyled.

Examples

  • States

    David is on leave, so he cannot be picked.

    Pick a reviewer.

    import { Combobox } from '@/components/klarhimmel/ui/combobox';
    import { Field } from '@/components/klarhimmel/ui/input';
    
    const staff = ['Astrid Nyberg', 'Bo Lindgren', 'Cecilia Åberg', 'David Ek', 'Elin Sjöberg', 'Fredrik Lund', 'Greta Wahl', 'Hugo Mård'].map((n) => ({ value: n, label: n, disabled: n === 'David Ek' }));
    
    export default function ComboboxStates() {
      return (
        <div className="grid w-[min(40rem,calc(100cqw-3rem))] min-h-72 content-start gap-6 @md:grid-cols-2">
          <Field label="Assign to" hint="David is on leave, so he cannot be picked.">{(p) => <Combobox {...p} options={staff} defaultValue="Greta Wahl" emptyText="Nobody by that name" />}</Field>
          <Field label="Reviewer" error="Pick a reviewer.">{(p) => <Combobox {...p} options={staff} placeholder="Type a name" />}</Field>
        </div>
      );
    }
  • Long

    Scroll the list, or type a few letters.

    import { Combobox } from '@/components/klarhimmel/ui/combobox';
    import { Field } from '@/components/klarhimmel/ui/input';
    
    const towns = ['Arvika', 'Borås', 'Eskilstuna', 'Falun', 'Gävle', 'Halmstad', 'Jönköping', 'Karlstad', 'Linköping', 'Malmö', 'Norrköping', 'Örebro', 'Piteå', 'Skellefteå', 'Sundsvall', 'Uppsala', 'Västerås', 'Växjö', 'Ystad', 'Åre', 'Östersund'].map((t) => ({ value: t, label: t }));
    
    export default function ComboboxLong() {
      return (
        <div className="flex min-h-80 w-[min(24rem,calc(100cqw-3rem))] flex-col">
          <Field label="Nearest town" hint="Scroll the list, or type a few letters.">{(p) => <Combobox {...p} options={towns} placeholder="Type a town" />}</Field>
        </div>
      );
    }

API reference

PropTypeDefaultDescription
options{ value: string; label: string; disabled?: boolean }[]-Required. Matching is case-insensitive and anywhere in the label.
value / defaultValue / onValueChangestring / (value: string) => void-Controlled or uncontrolled state. An empty string means nothing chosen.
placeholder / emptyTextstring- / 'No matches'Placeholder; the message when nothing matches.
namestring-Submits the chosen value with a form (a hidden input).
invalid / disabledbooleanfalseError styling; switches it off.
classNamestring-Extra classes, merged after the component's own.

Accessibility

KeyDoes
TypeOpens the list and filters it; the first match is highlighted
Down / UpOpens the list, or moves the highlight
EnterChooses the highlighted option
EscapeCloses and restores the chosen text; again, clears the field
Home / End / Left / RightMove the caret in the text, as in any field
TabLeaves the field; unmatched text is replaced by the chosen label

The WAI-ARIA combobox with list autocomplete. The number of matches is announced as you type.

Accessibility