Introduction

Getting Started

Introduction

goopUI is the shadcn catalog made gooey. Same components, same API, built on reka-ui, with a small physics engine that makes them swell, squish, and drip.

goopUI copies shadcn's components one by one. Each one wraps the same reka-ui primitive that shadcn-vue uses and keeps the same API: cva variants, class passthrough, and asChild. The difference is a layer of goo behind it. Hover a button and drips swell off its bottom edge, neck, and fall. Press it and it squishes, then springs back.

GoopButton, running on @goopui/core

How it works

Every component has two layers:

  • Your content. The real button, label, and icon. Nothing filters it, so text stays crisp and accessible.
  • The goo layer. An aria-hidden element under the content with pointer-events: none, holding the body shape and drip circles. An SVG goo filter (blur, then a sharp alpha threshold) melts the circles into one liquid shape.

The goo layer is padded outside the element, so drips can hang and fall without changing your layout. Its color is the variant's background token (--primary, --destructive, and so on).

It also reacts to how you move: fast entries splash goo out the far side, clicks send a shockwave, and the goo carries your pointer's momentum. An optional oil finish adds an iridescent sheen.

Drips run on a small spring and gravity integrator. Every instance on the page shares one requestAnimationFrame loop, and that loop stops when nothing is moving. It draws with an SVG filter by default (only transform and opacity change per frame) or with a shared WebGL canvas.

Packages

PackageWhat it isStatus
@goopui/coreThe engine. Plain TypeScript and DOM, with no framework and no dependencies. SSR-safe.Alpha
@goopui/vueVue components (SFCs) on top of core and reka-ui. Works in plain Vue/Vite.Alpha
@goopui/nuxtNuxt module that auto-imports the Vue components and injects goop.css.Alpha
@goopui/reactReact componentsLater
@goopui/svelteSvelte componentsLater

Components

Each component keeps the shadcn-vue API and adds goo props. Every effect can be switched off on its own, and :goo="false" renders the plain shadcn component.

ComponentWhat the goo does
ButtonDrips off the edge, squishes on press, pulls taffy.
SpinnerMetaballs chase each other around a circle, merging and splitting.
TogglePressing floods the button with goo. A toggle group's goo flows between items. New.
SwitchThe thumb is a goo blob that throws itself across the track.
CheckboxChecking drops goo into the box.
Radio groupThe selected dot jumps to the next radio over a goo bridge.
InputFocus runs goo around the field's border. Textarea too. New.
SliderA liquid fill and a jelly thumb.
ProgressA liquid fill with a wobbling front and drips.
SelectThe list blooms out of the selected item, and the check mark plops in. New.
PopoverBlooms open from a droplet and splashes all around.
Dropdown menuBlooms open, and the highlight slides between items.
TooltipBlooms open, quick and small.
TabsThe active indicator slides between tabs with momentum.
AccordionThe trigger row floods with goo and the content opens on a jelly spring. Collapsible too. New.
DialogThe panel blooms in the middle of the screen, with an opt-in goo overlay. New.
Alert dialogThe dialog's bloom, calmer. New.
SheetBlooms out of the screen edge. New.
DrawerA draggable drawer whose edge lags, sloshes, and splashes. New.
ToasterToasts drop in, merge when stacked, and drip away.
Page transitionA band of bubbly goo sweeps across the page on navigation. New.
BlobAmbient goo for backgrounds. Soon.

Blob comes last. It's also the performance stress test that decides whether a canvas renderer is needed.

Browser support

The last two versions of Chrome, Edge, Firefox, Safari, and iOS Safari. It has to feel good in Safari. If a filter is too slow there, that component falls back to static goo.