Getting Started
All Components
0
Active users
0.00%
Avg. uptime
+0
Built with tent ui
Installation
npx shadcn@latest add https://tentui.com/r/animated-counter.json
Usage
import { AnimatedCounter } from "@/components/animated-counter";
export function HeroStat() {
return (
<p className="text-4xl font-semibold">
<AnimatedCounter value={12480} /> users
</p>
);
}The counter holds the starting value until it scrolls into view (50% visible). Then it animates to value over duration seconds and stays put — once is the default. Pass once={false} if you want it to replay every time it re-enters the viewport.
Patterns
Locale formatting
The default rendering is plain toFixed(decimals). For thousands separators or currency formatting, pass a format function.
<AnimatedCounter
value={12480}
format={(v) => v.toLocaleString("en-US")}
/>
<AnimatedCounter
value={1299}
decimals={2}
format={(v) =>
v.toLocaleString("en-US", {
style: "currency",
currency: "USD"
})
}
/>Decimals (uptime, ratings, percentages)
<AnimatedCounter value={99.98} decimals={2} />%
<AnimatedCounter value={4.7} decimals={1} /> / 5Replay on re-enter
<AnimatedCounter value={500} once={false} />Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | — | Final value to animate to. Required. |
from | number | 0 | Starting value before the animation begins. |
duration | number | 1.6 | Animation duration in seconds. |
decimals | number | 0 | Decimal places to render when no format function is supplied. |
format | (value: number) => string | — | Custom formatter — overrides decimals. Use for locale or currency output. |
once | boolean | true | Run only the first time the counter scrolls into view. |
ease | "linear" | "easeIn" | "easeOut" | "easeInOut" | "easeOut" | Easing curve passed to animate(). |
Performance & accessibility
- The animation only starts when the element is in the viewport (via
useInViewwith a 50% threshold), so a long page with many counters won't kick off all animations on mount. tabular-numsis set on the rendered span so digits don't shift left/right as values change — avoids layout jitter at higher values.- The visible text is the live value, so screen readers and copy/paste both see the final number after the animation settles. If you need an immediate, announceable value, render the final number in a visually hidden sibling.