Combobox

Components

Combobox New · experimental

shadcn's Combobox with goo. The list blooms open below the input, the highlight is a blob, the check mark plops in, and the input gets a goo focus ring.

Type to filter, move between items, then pick one.

Customize

Preset
Speed 1.00×

GoopCombobox wraps the same reka-ui Combobox primitive as shadcn-vue's Combobox, with the same API: filtering, keyboard navigation, by, multiple, and form submission work the same. Swap the imports and you're done. Install it like Command.

Usage

<script setup lang="ts">
const frameworks = [
  { value: 'nuxt', label: 'Nuxt' },
  { value: 'sveltekit', label: 'SvelteKit' },
  { value: 'astro', label: 'Astro' },
]
const framework = ref()
</script>

<template>
  <GoopCombobox v-model="framework" by="value">
    <GoopComboboxAnchor class="relative w-56">
      <GoopComboboxInput placeholder="Select framework..." :display-value="(v) => v?.label ?? ''" />
      <GoopComboboxTrigger class="absolute inset-y-0 right-0 px-2" aria-label="Show frameworks">▾</GoopComboboxTrigger>
    </GoopComboboxAnchor>
    <GoopComboboxList class="w-(--reka-combobox-trigger-width)">
      <GoopComboboxEmpty>No framework found.</GoopComboboxEmpty>
      <GoopComboboxGroup>
        <GoopComboboxItem v-for="f in frameworks" :key="f.value" :value="f">
          {{ f.label }}
          <GoopComboboxItemIndicator />
        </GoopComboboxItem>
      </GoopComboboxGroup>
    </GoopComboboxList>
  </GoopCombobox>
</template>

The parts are GoopCombobox, Anchor, Input, Trigger, Cancel, List, Empty, Group, Item, ItemIndicator, and Separator, each prefixed with GoopCombobox. The list's goo lives on GoopComboboxList; the input's ring on GoopComboboxInput.

Behavior

  • Bloom. The list pools from its own center, spreads with a lobed edge, overshoots, and splashes all around, kept off the input. Close pulls it in and shrinks it back (openSlosh, openDrips, closeSlosh).
  • Highlight. The active item's background is a goo blob that glides between items with the keys and the pointer (highlight, momentum, wobble, followPointer). When typing hides the highlighted item, the blob shrinks out.
  • Choose. Picking an item squishes the blob (pop), sets the value at once, and closes the list once the pop has played. Its check mark plops in (plop).
  • Input ring. GoopComboboxInput gets GoopInput's goo focus ring (:goo="false" on it turns the ring off).

Turning parts off

<GoopComboboxList :plop="false">...</GoopComboboxList>        <!-- the check mark just appears -->
<GoopComboboxList :highlight="false">...</GoopComboboxList>   <!-- shadcn's flat highlight -->
<GoopComboboxList :open-drips="false">...</GoopComboboxList>  <!-- no splash -->
<GoopComboboxInput :goo="false" />                            <!-- no ring -->
<GoopComboboxList :goo="false">...</GoopComboboxList>         <!-- plain shadcn list -->

Props

GoopComboboxList takes reka-ui's ComboboxContent props (defaults position="popper", align="start", sideOffset 4), class, and the same goo props as GoopSelectContent except bloomFrom: a combobox list always blooms from its own center. GoopComboboxInput takes reka-ui's ComboboxInput props, GoopInput's ring props, and goo. The other parts take shadcn-vue's props.

GoopComboboxList goo props
PropTypeDefaultNotes
goobooleantrueMaster switch. false renders a plain shadcn ComboboxList.
flowbooleantrueThe list opens in flowStyle. false: it inflates from a bulb at the input edge.
flowStyle'bloom' | 'neck''bloom''bloom' pools from the list's own center, spreads with a lobed edge, overshoots, then splashes. 'neck' runs out of the input through a thick neck.
bloomStart'single' | 'merge''single''single': one droplet swells. 'merge': a few drops fuse.
bridgebooleantrueThe bulb and lumpy neck between the input and the list. Not used by bloom.
wobblebooleantrueJelly overshoot: the open overshoot and settle, the highlight overshoot, and the plop wobble.
momentumbooleantrueThe panel's launch (not used by bloom), and the highlight's stretch and neck while it travels.
fadebooleantrueThe items fade in once the goo settles.
openSloshbooleantrueOn open, every edge overshoots, rebounds, and wobbles.
openDripsbooleantrueOn open, the edges splash: globs, drops, and specks fling off and fall away, kept off the input.
openDripAmountnumber1Splash drop count, 0 to 3.
openDripSizenumber1Splash drop size, 0.3 to 2.5.
openDripFlingnumber1How far the drops are flung, 0 to 3.
openDripVarietynumber0.7Mix of globs and specks, 0 to 1. 0 makes every drop alike.
sloshVarietynumber1How differently each side bulges on open and close, 0 to 1. 0 is an even, centred bulge.
closeSloshbooleantrueOn close, the list pulls in and wobbles before it shrinks back.
highlightbooleantrueThe item highlight is a goo blob that glides between items with the keys and the pointer, and shrinks out when typing hides its item. false gives shadcn's flat background.
popbooleantrueThe highlight squishes when an item is chosen, before the list closes.
followPointerbooleantrueThe highlight's swell follows the pointer with a little lag. Off, or from the keyboard, it stays centred.
plopbooleantrueThe chosen item's check mark plops in: falls in small, squashes as it lands, and jiggles back to round.
surface'popover' | 'trigger' | string'popover'The list's color. 'popover' uses shadcn's popover tokens. Any CSS color works.
speednumber1Animation speed multiplier.
preset'subtle' | 'playful' | 'wild'app config, then playfulThe overall feel.
renderer'svg' | 'canvas''svg'How the goo is drawn.

Accessibility

The combobox and listbox roles, aria-activedescendant, keyboard navigation, and focus handling come from reka-ui. The goo surface, highlight blob, and ring are aria-hidden and can't be clicked. Under reduced motion the list opens and closes plainly, the highlight snaps, there's no pop or plop, and the ring is static. See Accessibility & reduced motion.