0

happy travellers

React + MotionText & scroll

Count-up number

A number that counts up from zero to your figure as soon as it scrolls into view.

Settings

2400
1200ms

Code

import { useEffect, useRef, useState } from "react";
import { useInView } from "framer-motion";

interface InViewCounterProps {
  end?: number;
  /** Milisegundos que tarda en llegar a la cifra final. */
  duration?: number;
  label?: string;
}

export default function InViewCounter({
  end = 2400,
  duration = 1200,
  label = "happy travellers",
}: InViewCounterProps) {
  const ref = useRef<HTMLDivElement>(null);
  // useInView mira el viewport y respeta el recorte de los contenedores con scroll: funciona en ambos casos.
  const seen = useInView(ref, { once: false });
  const [n, setN] = useState(0);

  useEffect(() => {
    if (!seen) {
      setN(0);
      return;
    }
    const start = performance.now();
    let raf = 0;
    const run = (t: number) => {
      const p = Math.min(1, (t - start) / duration);
      setN(Math.round(end * p));
      if (p < 1) raf = requestAnimationFrame(run);
    };
    raf = requestAnimationFrame(run);
    return () => cancelAnimationFrame(raf);
  }, [seen, duration, end]);

  return (
    <div ref={ref} className="text-center text-[#1d1d1f] dark:text-[#f5f5f7]">
      <strong className="text-5xl font-semibold tabular-nums">{n.toLocaleString("es-ES")}</strong>
      <p className="text-sm text-[#86868b]">{label}</p>
    </div>
  );
}

Prompt for AI

Paste it into ChatGPT, Claude or Cursor and it will adapt the block to your project with these settings, even if you don’t use React.

More in Text & scroll

Where to use it

Numbers that count up get read, while static ones get skimmed. Use it for the proof section of a landing page: “2,400 happy travelers”, projects delivered by a studio, meals served by a charity or downloads of an app. This animated number counter in React starts when the block becomes visible, not on page load.

Only use figures you can back up, since an animated number draws extra attention to it. Keep the duration around a second; longer and people scroll past before it ends. It counts again every time it comes back into view.