prince108 · No 108
Animated counter
Rolling digit counter with spring-loaded increments.
Install
pnpm dlx shadcn add @prince-ui/prince108Dependencies
Try it
- Press plus or minus — the old number leaves upward as the new one arrives from below.
- Click fast. Successive values overlap mid-flight instead of queueing behind each other.
- Go below zero; the minus sign rides along on the same swap.
How to use
AnimatedCounter owns its count and starts at 42, so it works the moment you drop it in — there is nothing to wire up and no props to pass.
All of the feel lives in one transition on the motion.span: stiffness decides how hard the digit snaps into place, damping how much it overshoots first. Drop damping toward 20 for a bouncier roll, raise stiffness for a sharper one.
Basic usage
import { AnimatedCounter } from "@/components/ui/prince108";
export default function Example() {
return <AnimatedCounter />;
}Driving the count from outside
The file is yours once it is installed, so when the number has to come from somewhere else — a cart, a form, a socket — replace the internal useState with props and leave the motion untouched.
export function AnimatedCounter({
value,
onChange,
}: {
value: number;
onChange: (next: number) => void;
}) {
return (
// ...same markup, with `count` swapped for `value`
// and the buttons calling `onChange(value ± 1)`
);
}Notes
- The whole number is keyed, not each column — going from 9 to 10 swaps the entire value rather than rolling the digits independently.
- AnimatePresence runs in "popLayout" mode, which pulls the outgoing number out of flow immediately. That is what lets rapid clicks overlap rather than stack up.
- tabular-nums holds every digit to the same width, so the value does not jitter sideways as it changes.
- The 4px blur on enter and exit is what reads as speed. Remove it for a flatter, more mechanical roll.
- overflow-hidden on the fixed 4rem × 6rem window is what clips the digits mid-travel — without it they fly past the card edge.
- There is no reduced-motion guard. Add one if the counter lands somewhere a visitor cannot scroll past.
Source code
The whole component is one file. Open it with the </> control at the top right of the preview, or take it straight from here.
Keep in mind
Several of these are recreations of interactions I admired elsewhere, rebuilt from scratch and usually extended. I have credited what I could trace — if something of yours is here uncredited, tell me and I will fix it.
License & usage
- Free to use and modify in personal and commercial projects.
- MIT licensed — no attribution required, though a link back is always welcome.
- No paywalled tier, no pro version, no lock-in.
Contact
Found a bug, or built something with it? Send me a note.