Components
Mechanical odometer-style animated counter. Each digit rolls independently with spring physics — the thousands digit starts first, ones digit resolves last, creating a satisfying cascade. Supports commas, decimals, prefixes ($, +), suffixes (%, M, ms), and scroll-triggered viewport entry. One line: <NumberTicker value={12480} suffix="+" />
npx shadcn@latest add https://21st.dev/r/dev.yadhakim/number-tickerLoading preview...
import { Component as NumberTicker } from "@/components/ui/number-ticker";
const stats = [
{ value: 12480, prefix: "", suffix: "+", label: "Active Users", decimals: 0 },
{ value: 2.4, prefix: "$", suffix: "M", label: "Revenue", decimals: 1 },
{ value: 99.98, prefix: "", suffix: "%", label: "Uptime", decimals: 2 },
{ value: 340, prefix: "", suffix: "ms", label: "Avg Latency", decimals: 0 },
];
export default function Demo() {
return (
<div className="flex min-h-screen items-center justify-center bg-background p-8">
<div className="w-full max-w-4xl">
<div className="text-center mb-16">
<h2 className="text-2xl font-bold tracking-tight text-foreground mb-2">
Number Ticker
</h2>
<p className="text-sm text-muted-foreground max-w-md mx-auto">
Mechanical odometer-style rolling digits. Each digit animates independently with spring physics. Scroll-triggered.
</p>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
{stats.map((stat, i) => (
<div
key={stat.label}
className="flex flex-col items-center gap-2 rounded-2xl border border-border bg-card p-8"
>
<NumberTicker
value={stat.value}
prefix={stat.prefix}
suffix={stat.suffix}
decimals={stat.decimals}
delay={i * 0.15}
className="text-4xl md:text-5xl text-foreground"
/>
<span className="text-xs font-medium text-muted-foreground tracking-wide uppercase">
{stat.label}
</span>
</div>
))}
</div>
{/* Additional showcase — different sizes and directions */}
<div className="mt-12 flex flex-wrap items-baseline justify-center gap-8">
<div className="text-center">
<NumberTicker
value={7}
suffix=" days"
className="text-6xl text-foreground"
delay={1}
/>
<p className="text-xs text-muted-foreground mt-2">Free Trial</p>
</div>
<div className="text-center">
<NumberTicker
value={500}
prefix="+"
className="text-6xl text-foreground"
delay={1.2}
/>
<p className="text-xs text-muted-foreground mt-2">Integrations</p>
</div>
<div className="text-center">
<NumberTicker
value={24}
suffix="/7"
className="text-6xl text-foreground"
delay={1.4}
/>
<p className="text-xs text-muted-foreground mt-2">Support</p>
</div>
</div>
</div>
</div>
);
}