Monthly revenue
Customize
Usage
import { NumberTicker } from "@/components/motionkit/number-ticker";
<NumberTicker value={48200} prefix="$" />Motion
The exact curves this component runs, plotted with Motion's own maths.
Count up
Scrolls into view
Works in the App Router as is. Requires Tailwind CSS v4.
npm install motionSave as components/motionkit/number-ticker.tsx.
"use client";
import { useEffect, useRef } from "react";
import { animate, motion, useInView, useMotionValue, useReducedMotion, useTransform } from "motion/react";
type NumberTickerProps = {
value: number;
/** Seconds to count up. */
duration?: number;
decimals?: number;
prefix?: string;
suffix?: string;
/** BCP 47 locale used for thousands separators. */
locale?: string;
className?: string;
};
export function NumberTicker({
value,
duration = 1.6,
decimals = 0,
prefix = "",
suffix = "",
locale = "en-US",
className = "",
}: NumberTickerProps) {
const ref = useRef<HTMLSpanElement>(null);
// wait until the whole number is on screen: counting as soon as a sliver peeks in
// means the count is over before anyone has scrolled far enough to see it
const inView = useInView(ref, { once: true, amount: "all" });
const reduceMotion = useReducedMotion();
const count = useMotionValue(0);
const format = (number: number) =>
`${prefix}${number.toLocaleString(locale, {
minimumFractionDigits: decimals,
maximumFractionDigits: decimals,
})}${suffix}`;
const formatted = useTransform(count, format);
useEffect(() => {
if (!inView) return;
if (reduceMotion) {
count.set(value);
return;
}
const controls = animate(count, value, { duration, ease: [0.16, 1, 0.3, 1] });
return () => controls.stop();
}, [inView, value, duration, reduceMotion, count]);
return (
<span ref={ref} className={`tabular-nums ${className}`}>
{/* screen readers hear the final value, not every frame of the count */}
<motion.span aria-hidden>{formatted}</motion.span>
<span className="sr-only">{format(value)}</span>
</span>
);
}
| Prop | Type | Default | Description |
|---|---|---|---|
| value | number | — | The number to count to. |
| duration | number | 1.6 | Seconds to count up. |
| decimals | number | 0 | Fixed decimal places. |
| prefix / suffix | string | "" | Text before and after the number. |
| locale | string | "en-US" | Locale used for thousands separators. |
Small details make the whole thing feel alive.
Word Rise
Words lift out of a blur one after another as the line scrolls into view.
Split-Flap Display
An airport departures board: every tile flips through its drum, one character at a time, to land on your text.
Make every word land.
Kinetic Headline
Headlines that arrive word by word or letter by letter: slide, blur, hinge or rise from behind a mask, from any direction.