Overview
Animated Counter counts from a start value to its target when it scrolls into view (or on mount). Frames are written directly to the DOM through requestAnimationFrame, so React never re-renders per frame. Formatting goes through Intl.NumberFormat for locale-correct grouping and decimals, an invisible copy of the widest value reserves space so surrounding text stays put, and screen readers always receive the final value rather than a stream of intermediate numbers. When the target changes, the counter animates from the number on screen — never back from zero.
01Installation
Method 01 · Registry
CLI verifiedUses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/animated-counter.tsx and adds its dependencies.
npx shadcn@latest add https://wancraft.my/r/animated-counter.jsonMethod 02 · Copy and paste
- 1. No extra dependencies — React and Tailwind CSS only.
- 2. Copy the source from the Code tab into
components/ui/animated-counter.tsx. - 3. Import it and adjust props.
- Dependencies
- None
- Peer
- react ≥ 18, tailwindcss ≥ 4
- Added
- 1 Oct 2026
- Updated
- —
02Usage
The default configuration. Change properties above to generate a tailored example in the Usage tab.
import { AnimatedCounter } from "@/components/ui/animated-counter";
export function AnimatedCounterExample() {
return (
<AnimatedCounter
value={12480}
className="text-5xl font-semibold tracking-tight"
/>
);
}03Props
| Prop | Type | Default | Description |
|---|---|---|---|
| value | number | — | Target number. Changing it animates from the value currently on screen. |
| from | number | 0 | Start value for the first play. |
| decimals | number | 0 | Fixed number of fraction digits. |
| prefix | string | "" | Text before the number, e.g. a currency symbol. |
| suffix | string | "" | Text after the number, e.g. % or +. |
| trigger | "view" | "mount" | "view" | Play once when 40% visible, or immediately on mount. |
| duration | number | 2000 | Animation length in milliseconds. |
| easing | "linear" | "easeOut" | "easeInOut" | "expo" | "easeOut" | Easing curve. |
| locale | string | "en-US" | BCP 47 locale passed to Intl.NumberFormat. |
| grouping | boolean | true | Use locale grouping separators. |
| ...props | HTMLAttributes<HTMLSpanElement> | — | Forwarded to the root <span> (className, id, aria-*). |
04Features
- 01Plays once when scrolled into view, or on mount
- 02Locale-aware formatting via Intl.NumberFormat
- 03Reserved width and tabular numerals — zero layout shift
- 04Per-frame updates bypass React rendering
- 05Retargets smoothly from the on-screen value
05Accessibility
- The animated digits are aria-hidden; a visually hidden copy exposes the final formatted value to assistive technology.
- With prefers-reduced-motion the final value is shown immediately.
- Renders inline text, so it inherits the surrounding font, size and colour.