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.
Customize
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
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
@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.
| Prop | Type | Default | Notes |
|---|---|---|---|
goo | boolean | true | Master switch. false renders plain shadcn Command parts. |
highlight | boolean | true | The active item is a goo blob that glides between items, following the arrow keys and the pointer. false gives shadcn's flat background. |
pop | boolean | true | The highlight squishes when an item is chosen. |
ring | boolean | true | The search input gets GoopInput's goo focus ring. A GoopCommandInput's own goo prop overrides it. |
wobble | boolean | true | Jelly overshoot: the highlight's overshoot and grow-in, and the ring's idle wobble. |
momentum | boolean | true | The highlight stretches and necks toward where it came from as it travels. |
followPointer | boolean | true | The highlight's swell follows the pointer with a little lag. From the keyboard it stays centred. |
speed | number | 1 | Animation speed multiplier. |
preset | 'subtle' | 'playful' | 'wild' | app config, then playful | The 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.