Back to library
text
Counting Number
Animates from a start value to a target number with a tween, formatted with thousands separators. Replayable via ref.
Inspired by Cnippet UI · View source
Preview
0
Downloads
0%
Uptime
Copy for AI
Copy for Lovable is the fastest way to move this component into another Lovable project — it copies the full prompt with dependencies, files, and a usage example. Copy all files gives you the raw source if you want to paste it manually.
Installation
npx shadcn@latest add @cnippet/counting-numberPaste into a terminal — or paste into a Lovable chat and I'll run it for you.
Source files
components/ui/counting-number.tsx
"use client";
import {
type AnimationPlaybackControls,
animate,
motion,
useMotionValue,
useTransform,
type ValueAnimationTransition,
} from "motion/react";
import {
forwardRef,
useCallback,
useEffect,
useImperativeHandle,
useRef,
} from "react";
import { cn } from "@/lib/utils";
export type CountingNumberRef = {
startAnimation: () => void;
};
export type CountingNumberProps = {
from?: number;
target: number;
transition?: ValueAnimationTransition;
className?: string;
onStart?: () => void;
onComplete?: () => void;
autoStart?: boolean;
};
export const CountingNumber = forwardRef<
CountingNumberRef,
CountingNumberProps
>(
(
{
from = 0,
target = 100,
transition = { duration: 3, ease: "easeInOut", type: "tween" },
className,
onStart,
onComplete,
autoStart = true,
...props
},
ref,
) => {
const count = useMotionValue(from);
const rounded = useTransform(count, (latest) =>
Math.round(latest).toLocaleString(),
);
const controlsRef = useRef<AnimationPlaybackControls | null>(null);
const startAnimation = useCallback(() => {
controlsRef.current?.stop();
onStart?.();
count.set(from);
controlsRef.current = animate(count, target, {
...transition,
onComplete: () => onComplete?.(),
});
}, [from, target, transition, onStart, onComplete, count]);
useImperativeHandle(ref, () => ({ startAnimation }));
useEffect(() => {
if (autoStart) startAnimation();
return () => controlsRef.current?.stop();
}, [autoStart, startAnimation]);
return (
<motion.span className={cn("tabular-nums", className)} {...props}>
{rounded}
</motion.span>
);
},
);
CountingNumber.displayName = "CountingNumber";
Dependencies
motion