Number Ticker

Counts up to any number when it scrolls into view.

0+

Active teams

0.00%

Uptime

0.0

Average rating

Installation

  1. Install the dependencies

    $ npm i motion lucide-react
  2. Copy the source into components/ui/number-ticker.tsx

    components/ui/number-ticker.tsx
    "use client";
    
    import { animate, motion, useInView, useMotionValue, useTransform } from "motion/react";
    import { useEffect, useRef } from "react";
    
    type NumberTickerProps = {
      value: number;
      /** Animation length in seconds */
      duration?: number;
      decimals?: number;
      prefix?: string;
      suffix?: string;
      className?: string;
    };
    
    export function NumberTicker({
      value,
      duration = 2,
      decimals = 0,
      prefix = "",
      suffix = "",
      className = "",
    }: NumberTickerProps) {
      const ref = useRef<HTMLSpanElement>(null);
      const inView = useInView(ref, { once: true, amount: 0.6 });
      const count = useMotionValue(0);
      const display = useTransform(
        count,
        (v) =>
          `${prefix}${v.toLocaleString("en-US", {
            minimumFractionDigits: decimals,
            maximumFractionDigits: decimals,
          })}${suffix}`,
      );
    
      useEffect(() => {
        if (!inView) return;
        const controls = animate(count, value, { duration, ease: [0.16, 1, 0.3, 1] });
        return () => controls.stop();
      }, [inView, value, duration, count]);
    
      return (
        <motion.span ref={ref} className={`tabular-nums ${className}`}>
          {display}
        </motion.span>
      );
    }
    
    export default function Demo() {
      const stats = [
        { value: 12480, label: "Active teams", suffix: "+" },
        { value: 99.99, label: "Uptime", suffix: "%", decimals: 2 },
        { value: 4.9, label: "Average rating", decimals: 1 },
      ];
      return (
        <div className="grid w-full max-w-2xl grid-cols-1 gap-8 text-center sm:grid-cols-3">
          {stats.map((s) => (
            <div key={s.label}>
              <NumberTicker
                value={s.value}
                suffix={s.suffix}
                decimals={s.decimals}
                className="text-4xl font-semibold tracking-tight text-zinc-900"
              />
              <p className="mt-1 text-sm text-zinc-500">{s.label}</p>
            </div>
          ))}
        </div>
      );
    }
    
  3. Use it

    The file exports the component and a Demo showing typical usage. Import whichever you need.