Select

Components

Select New · experimental

shadcn's Select with goo. The list blooms out of the value you picked, the highlight is a blob, and the check mark plops in.

Open it, move between items, then pick one.

Customize

Preset
Speed 1.00×
Flow style
Surface

GoopSelect wraps the same reka-ui Select primitive as shadcn-vue's Select, with the same API. Keyboard navigation, typeahead, focus handling, form submission, and positioning work the same. 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 GoopSelect* part, adds goop.css, and points Tailwind at the component classes. It needs Tailwind CSS v4 and the shadcn tokens (--popover, --popover-foreground, --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:
import { ref } from 'vue'
import {
  GoopSelect,
  GoopSelectContent,
  GoopSelectGroup,
  GoopSelectItem,
  GoopSelectLabel,
  GoopSelectTrigger,
  GoopSelectValue,
} from '@goopui/vue'

const fruit = ref('Blueberry')
</script>

<template>
  <GoopSelect v-model="fruit">
    <GoopSelectTrigger class="w-44">
      <GoopSelectValue placeholder="Pick a fruit" />
    </GoopSelectTrigger>
    <GoopSelectContent>
      <GoopSelectGroup>
        <GoopSelectLabel>Fruits</GoopSelectLabel>
        <GoopSelectItem value="Apple">Apple</GoopSelectItem>
        <GoopSelectItem value="Banana">Banana</GoopSelectItem>
        <GoopSelectItem value="Blueberry">Blueberry</GoopSelectItem>
      </GoopSelectGroup>
    </GoopSelectContent>
  </GoopSelect>
</template>

The parts are GoopSelect, Trigger, Value, Content, Group, Label, Item, ItemText, Separator, ScrollUpButton, and ScrollDownButton, each prefixed with GoopSelect. All the goo lives on GoopSelectContent.

Behavior

  • Bloom. The list pools from a single droplet (or a few that fuse, bloomStart="merge"), spreads out with a lobed edge, overshoots, and wobbles to rest. Then it splashes drops all around, kept off the trigger (openSlosh, openDrips).
  • Highlight. The highlighted item's background is a goo blob. It slides between items with a stretch and a neck, overshoots when it lands, and follows the keyboard as well as the pointer (highlight, momentum, wobble).
  • Follow the pointer. As the pointer moves along an item, the blob's swell leans toward it with a little lag (followPointer).
  • Choose. Picking an item squishes the blob (pop), sets the value at once, and closes the list once the pop has played. The new item's check mark plops in: it falls in small, squashes flat as it lands, and jiggles back to round (plop).
  • Close. The list pulls in, wobbles, and shrinks back to where it bloomed from (closeSlosh).
  • Content stays sharp. The list's background, ring, and shadow are drawn on an aria-hidden surface behind the items, so the text is never filtered.

Position and bloom origin

position is reka-ui's. With item-aligned (the default, like shadcn), the list is laid over the trigger with the selected item on top of it, and it blooms out of that item, so the list grows from the value you clicked. With popper, the list sits below the trigger like a menu and blooms from its own center:

<GoopSelectContent>...</GoopSelectContent>                    <!-- item-aligned: blooms from the selected item -->
<GoopSelectContent position="popper">...</GoopSelectContent>  <!-- blooms from its center -->

bloomFrom overrides where the bloom starts: 'auto' (the default, as above), 'selected', or 'center'. Close shrinks back to the same spot.

<GoopSelectContent bloom-from="center">...</GoopSelectContent>

Surface color

A select trigger is an outlined field, so its color isn't the list's. Unlike Popover and Dropdown Menu, the list defaults to surface="popover": shadcn's popover tokens. 'trigger' takes the trigger's background and text color, and any CSS color works too:

<GoopSelectContent>...</GoopSelectContent>                                <!-- 'popover' (default) -->
<GoopSelectContent surface="trigger">...</GoopSelectContent>
<GoopSelectContent surface="oklch(0.62 0.19 150)">...</GoopSelectContent>

Turning parts off

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

<GoopSelectContent :plop="false">...</GoopSelectContent>              <!-- the check mark just appears -->
<GoopSelectContent :follow-pointer="false">...</GoopSelectContent>    <!-- the blob stays centred -->
<GoopSelectContent :highlight="false">...</GoopSelectContent>         <!-- shadcn's flat highlight -->
<GoopSelectContent :open-drips="false">...</GoopSelectContent>        <!-- no splash -->

Plain shadcn

:goo="false" renders exactly shadcn's SelectContent, with its own enter and exit animation:

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

Driving it from code

GoopSelectContent exposes its engine handle as handle (null before it mounts, or when goo is off). The list only exists while open.

Props

The root, trigger, value, group, label, item, separator, and scroll buttons take shadcn-vue's props. GoopSelectTrigger also takes size: 'default' | 'sm', like shadcn. GoopSelectContent takes reka-ui's SelectContent props (position, side, align, sideOffset, and the rest), class, and these goo props. Defaults are position="item-aligned" and align="center".

GoopSelectContent goo props
PropTypeDefaultNotes
goobooleantrueMaster switch. false renders a plain shadcn SelectContent.
flowbooleantrueThe list opens in flowStyle. false: it inflates from a bulb at the trigger edge.
flowStyle'bloom' | 'neck''bloom''bloom' pools from a point and spreads in every direction with a lobed edge, overshoots, then splashes. 'neck' runs out of the trigger through a thick neck.
bloomStart'single' | 'merge''single''single': one droplet swells. 'merge': a few drops fuse.
bloomFrom'auto' | 'selected' | 'center''auto'Where the bloom starts. 'auto': the selected item for an item-aligned list, the list's center for a popper. Close shrinks back to the same spot.
bridgebooleantrueThe bulb and lumpy neck between the trigger 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, the edges (bloom: every edge) overshoot, rebound, and wobble.
openDripsbooleantrueOn open, the edges splash: globs, drops, and specks fling off and fall away, kept off the trigger.
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 (off-centre, one or two lumps, staggered), 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 slides between items and follows the keyboard. false gives shadcn's flat background.
popbooleantrueThe highlight squishes when an item is chosen, before the list closes.
followPointerbooleantrueThe highlight's swell (its neck while it slides, a soft bulge as the pointer moves along an item) 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. 'trigger' takes the trigger's background and text color. 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

Keyboard navigation, typeahead, focus handling, form submission, and ARIA attributes come from reka-ui. The goo surface and the highlight blob are aria-hidden and can't be clicked, and the items are never filtered. Under reduced motion the list opens and closes plainly: it keeps its goo shape and color, but the bloom, slosh, and splash are off, the highlight snaps between items, and there's no pop and no plop. See Accessibility & reduced motion.