Command

Components

Command New · experimental

shadcn's Command with goo. The active item is a blob that glides between items with the keys and the pointer, the search box gets a goo focus ring, and the dialog blooms open.

Use the arrow keys or the pointer, then type to filter.

Customize

Preset
Speed 1.00×
Colour

GoopCommand wraps the same reka-ui Listbox primitive as shadcn-vue's Command, with the same API and the same filter: every item registers its text, typing hides items and groups that don't match, and GoopCommandEmpty shows when nothing does. Swap the imports and you're done.

Installation

Nuxt

pnpm add @goopui/nuxt
nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@goopui/nuxt'],
})

The module auto-imports every GoopCommand* part, adds goop.css, and points Tailwind at the component classes. It needs Tailwind CSS v4 and the shadcn tokens (--popover, --accent, --input, --ring, --radius).

Vue

pnpm add @goopui/vue
main.css
@import "tailwindcss";
@import "@goopui/vue/goop.css";

Usage

<script setup lang="ts">
// Nuxt auto-imports these. In plain Vue, import them from '@goopui/vue'.
import {
  GoopCommand,
  GoopCommandEmpty,
  GoopCommandGroup,
  GoopCommandInput,
  GoopCommandItem,
  GoopCommandList,
  GoopCommandSeparator,
  GoopCommandShortcut,
} from '@goopui/vue'
</script>

<template>
  <GoopCommand class="max-w-sm rounded-lg border shadow-md">
    <GoopCommandInput placeholder="Type a command or search..." />
    <GoopCommandList>
      <GoopCommandEmpty>No results found.</GoopCommandEmpty>
      <GoopCommandGroup heading="Suggestions">
        <GoopCommandItem value="calendar">Calendar</GoopCommandItem>
        <GoopCommandItem value="emoji">Search Emoji</GoopCommandItem>
      </GoopCommandGroup>
      <GoopCommandSeparator />
      <GoopCommandGroup heading="Settings">
        <GoopCommandItem value="profile">
          Profile
          <GoopCommandShortcut>⌘P</GoopCommandShortcut>
        </GoopCommandItem>
      </GoopCommandGroup>
    </GoopCommandList>
  </GoopCommand>
</template>

The parts are GoopCommand, Input, List, Empty, Group, Item, Separator, Shortcut, and Dialog, each prefixed with GoopCommand. The goo settings live on GoopCommand (or GoopCommandDialog), and its parts read them.

Behavior

  • Highlight. The active item's background is a goo blob. It glides between items as the arrow keys or the pointer move the highlight, stretches with a neck while it travels, and overshoots a little when it lands (highlight, momentum, wobble). The pointer moves the highlight too (highlightOnHover, on by default, like cmdk).
  • Follow the pointer. As the pointer moves along an item, the blob's swell leans toward it with a little lag (followPointer). From the keyboard it stays centred.
  • Filter. When typing hides the highlighted item, the blob shrinks out and grows in again on the first match.
  • Choose. Choosing an item squishes the blob (pop) and clears the search, like shadcn.
  • Search ring. The search box gets GoopInput's goo focus ring: it flows around the box from where you clicked, ripples as you type, and drains on blur (ring).

Dialog

GoopCommandDialog is shadcn's CommandDialog: a dialog with a screen-reader title and description around a command. It blooms open like Dialog: it pools from its center, overshoots every side, and splashes all around, then shrinks back on close.

<script setup lang="ts">
const open = ref(false)
</script>

<template>
  <GoopButton variant="outline" @click="open = true">Open the palette</GoopButton>
  <GoopCommandDialog v-model:open="open">
    <GoopCommandInput placeholder="Type a command or search..." auto-focus />
    <GoopCommandList>
      <GoopCommandEmpty>No results found.</GoopCommandEmpty>
      <GoopCommandGroup heading="Suggestions">
        <GoopCommandItem value="calendar" @select="open = false">Calendar</GoopCommandItem>
      </GoopCommandGroup>
    </GoopCommandList>
  </GoopCommandDialog>
</template>

It takes the dialog's bloom props (openSlosh, openDrips, closeSlosh, overlay, ...) and the command's toggles below. goo, wobble, speed, preset, and renderer drive both. title and description default to shadcn's.

Combobox

For a searchable select, see Combobox: the same highlight and ring, in a list that blooms open below the input.

Turning parts off

Every effect is its own boolean, and they all default to on:

<GoopCommand :highlight="false">...</GoopCommand>        <!-- shadcn's flat highlight -->
<GoopCommand :ring="false">...</GoopCommand>             <!-- no goo ring on the search -->
<GoopCommand :follow-pointer="false">...</GoopCommand>   <!-- the blob stays centred -->
<GoopCommandDialog :open-drips="false">...</GoopCommandDialog>  <!-- no splash -->

GoopCommandInput also takes GoopInput's ring props (flow, ripple, drain, ...) and its own goo switch.

Plain shadcn

:goo="false" renders exactly shadcn's Command:

<GoopCommand :goo="false">...</GoopCommand>

Props

GoopCommand takes reka-ui's ListboxRoot props (modelValue, multiple, highlightOnHover, ...), class, and these goo props. The other parts take shadcn-vue's props.

GoopCommand goo props
PropTypeDefaultNotes
goobooleantrueMaster switch. false renders plain shadcn Command parts.
highlightbooleantrueThe active item is a goo blob that glides between items, following the arrow keys and the pointer. false gives shadcn's flat background.
popbooleantrueThe highlight squishes when an item is chosen.
ringbooleantrueThe search input gets GoopInput's goo focus ring. A GoopCommandInput's own goo prop overrides it.
wobblebooleantrueJelly overshoot: the highlight's overshoot and grow-in, and the ring's idle wobble.
momentumbooleantrueThe highlight stretches and necks toward where it came from as it travels.
followPointerbooleantrueThe highlight's swell follows the pointer with a little lag. From the keyboard it stays centred.
speednumber1Animation speed multiplier.
preset'subtle' | 'playful' | 'wild'app config, then playfulThe overall feel.
renderer'svg' | 'canvas''svg'How the goo is drawn.

Accessibility

Keyboard navigation, aria-activedescendant on the search box, and the listbox roles come from reka-ui. The highlight blob and the ring are aria-hidden and can't be clicked, and the items are never filtered. Under reduced motion the highlight snaps between items with no pop, the ring is static, and the dialog opens plainly. See Accessibility & reduced motion.