Animated Number
Smoothly counts to a target number on mount using requestAnimationFrame with cubic ease-out. Pure React.
Use and inspectUse this when the motion supports the surrounding content. Connect callbacks and state to your app, keep the reset preview as a local demonstration, and add a reduced-motion fallback for movement or looping. The preview is synthetic; it does not call a backend.Next: inspect the source below and replace the demo inputs with your own data.
Animated NumberSmoothly counts to a target number on mount using requestAnimationFrame with cubic ease-out. Pure React.
"use client";
import { useState, useEffect, useRef } from "react";
type AnimatedNumberProps = {
value: number;
duration?: number;
format?: (n: number) => string;
};
function easeOut(t: number): number {
return 1 - Math.pow(1 - t, 3);
}
export function AnimatedNumber({ value, duration = 1.5, format }: AnimatedNumberProps) {
const [display, setDisplay] = useState(0);
const startRef = useRef<number | null>(null);
const rafRef = useRef<number>(0);
useEffect(() => {
startRef.current = null;
function tick(timestamp: number) {
if (startRef.current === null) startRef.current = timestamp;
const elapsed = (timestamp - startRef.current) / (duration * 1000);
const progress = Math.min(elapsed, 1);
setDisplay(Math.round(easeOut(progress) * value));
if (progress < 1) {
rafRef.current = requestAnimationFrame(tick);
}
}
rafRef.current = requestAnimationFrame(tick);
return () => cancelAnimationFrame(rafRef.current);
}, [value, duration]);
return (
<span className="mp-animated-number">
{format ? format(display) : String(display)}
</span>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| valuerequired | number | — | Target number to count to |
| duration | number | 1.5 | Count duration in seconds |
| format | (n: number) => string | — | Optional number formatter |