Liquid Blob Cursor Follow
A soft blurred blob that trails the cursor with spring-smoothed lag.
- Category
- Backgrounds
- Added
- 2026-08-22
- Deps
- 1
- Updated
- 2026-09-01
Props
"use client";
import { useRef } from "react";
import { motion, useMotionValue, useSpring } from "framer-motion";
export function LiquidBlobCursor({
size = 90,
softness = 18,
}: {
size?: number;
softness?: number;
}) {
const areaRef = useRef<HTMLDivElement>(null);
const x = useMotionValue(0);
const y = useMotionValue(0);
const springX = useSpring(x, { stiffness: 120, damping: 16, mass: 0.6 });
const springY = useSpring(y, { stiffness: 120, damping: 16, mass: 0.6 });
function handleMove(e: React.MouseEvent<HTMLDivElement>) {
const rect = areaRef.current?.getBoundingClientRect();
if (!rect) return;
x.set(e.clientX - rect.left);
y.set(e.clientY - rect.top);
}
return (
<div
ref={areaRef}
onMouseMove={handleMove}
className="relative h-64 w-full overflow-hidden rounded-2xl bg-neutral-100"
>
<motion.div
className="pointer-events-none absolute rounded-full bg-red-500"
style={{
width: size,
height: size,
x: springX,
y: springY,
translateX: "-50%",
translateY: "-50%",
filter: `blur(${softness}px)`,
opacity: 0.75,
}}
/>
</div>
);
}
About this background
This tracks the cursor with a spring rather than locking onto it 1:1, which is the entire trick: raw coordinates go straight into a Framer Motion useMotionValue via .set() inside the mousemove handler, never React state, since that would re-render on every pixel of movement — and each raw value is wrapped in its own useSpring (stiffness ~120, damping ~16, mass ~0.6) before it reaches the blob's x/y style, which is what turns a rigid cursor-lock into a soft, lagging trail. It's a different effect from this library's dot-trail cursor: one blurred shape following with lag, not a chain of discrete points marking the path already travelled — reach for this one for a single ambient glow behind hero content, the dot trail instead when the path itself should stay visible. Tracking is scoped to the container's own bounding rect via a ref, so the blob only follows the mouse while it's over that specific element; there's no window-level listener, so a full-viewport version needs the handler moved to window with viewport-relative coordinates. The heavy blur is core to the look, not a finishing touch — drop softness too low and it stops reading as liquid and starts reading as a plain circle chasing the pointer.
Curator’s note
Raw position lives in a useMotionValue updated directly in the mousemove handler, never React state — routing high-frequency pointer coordinates through setState would re-render the whole tree on every pixel of mouse movement. The spring wrapping is what turns a rigid cursor-lock into a liquid trailing follow.
Pairs well with
Matched on shared tags and category — the entries most likely to be used alongside this one.
Magnetic Button
componentsA button whose label leans toward the cursor with a springy pull.
Cursor Label Follower
componentsA contextual chip that trails the pointer and names whatever it is over.