Cursor
PointerA custom pointer that trails the real one: a dot, a ring that grows over links and buttons, or a label ("View") any element can ask for with data-cursor-label.
Draws a cluster of liquid drops that merge and pull apart: metaballs on the GPU with a compact kernel, cut with an edge exactly one pixel soft. Seven drops orbit a focus at their own pace; an eighth follows the pointer on a spring and the others lean toward it, so the cluster stretches after the cursor, necks form between drops, and it gathers again when the pointer leaves. Two tones: the body, and a core only where drops overlap. Every constant is a custom property re-read when the element's style changes. Without WebGL2 or JavaScript two still drops stand in. Fills its positioned parent.
Gooey.
move your cursor
Live, from the library itself: scroll, hover, navigate away and back. The demo is never gated.
Adjust · props are custom properties, set inline, so you edit the running instance
JavaScript with its libs
10.6 kB raw
1.1 kB raw of its own, 752 B gzip. The rest is the runtime, the shader runner, the canvas helper and the GL core, loaded once for every effect on the page.
CSS 5.2 kB raw including the base tokens. Measured from a production build, and again in CI for every change that can move it.
Browser support
Chrome 56 · Firefox 51 · Safari 15; needs webgl2. Elsewhere: without WebGL2, two still drops at the focus.
The demo above is never gated; the source is. With a key in components.json (see account), the registry serves it like any other item:
npx shadcn@latest add @moonarc-pro/gooey<section class="relative min-h-[60vh]">
<Gooey color="var(--primary)" focus="0.7 0.5" />
<h1 class="relative">Liquid drops that merge</h1>
</section>Without a key the registry answers 401 with an upgrade message. Related free primitives: cursor, magnetic.
| Prop | Type | Default | Description |
|---|---|---|---|
color | string | 'oklch(0.66 0.2 292)' | Drop colour; any CSS colour, theme tokens included. |
core | string | color lifted 45 % toward white | The colour where drops overlap. |
size | number | 1 | Drop size multiplier. |
speed | number | 1 | Orbit speed multiplier; accumulated, so a change never jumps. |
spread | number | 1 | How far the drops wander from the focus. |
follow | number | 0.6 | How much the cluster leans toward the pointer drop; 0 keeps it home. |
focus | string | '0.5 0.5' | Where the cluster sits, as fractions of the box (x y, from the top-left). |
No loop: the drops are drawn once at a fixed moment; the pointer drop stays home.
ClientRouterBound through the shared runtime: loop, observers and pointer listener are released before the swap and the WebGL context is lost on purpose after it; five round trips leave one context. Under transition:persist the canvas keeps its context and starts again on the next page.
Replaces: MetaBalls (React Bits) · BlobCursor gooey (React Bits) · SVG goo filter. See the migration table.
A custom pointer that trails the real one: a dot, a ring that grows over links and buttons, or a label ("View") any element can ask for with data-cursor-label.
A button or icon is pulled toward the cursor while it is nearby and springs back when it leaves.
Also: view transitions · how costs are measured · browser support · accessibility policy · five-minute setup · MCP for agents · this page as Markdown