Back to all components
Animated Number
Animated number with different effects
1

Animated Number

A text reveal component that reveals text with different effects.

Installation

Use the following command to install the component:

npx shadcn@latest add https://ui.sanjid.shop/r/animated-number.json
pnpm dlx shadcn@latest add https://ui.sanjid.shop/r/animated-number.json
yarn shadcn@latest add https://ui.sanjid.shop/r/animated-number.json
bunx --bun shadcn@latest add https://ui.sanjid.shop/r/animated-number.json

Install the following dependencies:

npm install motion
pnpm add motion
yarn add motion
bun add motion

Add util file

lib/utils.ts
1import { ClassValue, clsx } from "clsx";2import { twMerge } from "tailwind-merge";3
4export function cn(...inputs: ClassValue[]) {5  return twMerge(clsx(inputs));6}

Copy and paste the following code into your project.

components/ui/animated-number.tsx
1"use client";2
3import { cn } from "@/lib/utils";4import { motion } from "motion/react";5import React, { useMemo } from "react";6
7// ---------------------------------------------------------8// SIZE MAP — flexible for text scaling9// ---------------------------------------------------------10const sizeMap = {11  sm: { fontSize: "1.5rem", height: "1.5rem", width: "1.75ch" },12  md: { fontSize: "2rem", height: "2rem", width: "2ch" },13  lg: { fontSize: "3rem", height: "3rem", width: "2.75ch" },14  xl: { fontSize: "4rem", height: "4rem", width: "3.5ch" },15  "2xl": { fontSize: "5rem", height: "5rem", width: "4.5ch" },16} as const;17
18export type AnimatedSize = keyof typeof sizeMap;19
20interface AnimatedNumberProps {21  number: number | string;22  prefix?: string;23  suffix?: string;24  label?: string;25  size?: AnimatedSize;26  className?: string;27  digitClassName?: string;28  durationPerDigit?: number;29  blur?: boolean;30  direction?: "up" | "down"; // ⬆️⬇️ new feature31  easing?: [number, number, number, number]; // customizable motion easing32}33
34// ---------------------------------------------------------35// AnimatedNumber Component36// ---------------------------------------------------------37export const AnimatedNumber: React.FC<AnimatedNumberProps> = ({38  number,39  prefix = "",40  suffix = "",41  label,42  size = "xl",43  className,44  digitClassName,45  durationPerDigit = 0.6,46  blur = false,47  direction = "up",48  easing = [0.22, 1, 0.36, 1],49}) => {50  const digits = useMemo(() => number.toString().split(""), [number]);51  const sizeStyle = sizeMap[size];52
53  return (54    <div className={cn("flex flex-col items-start gap-1", className)}>55      <div className="flex items-end">56        {prefix && <StaticChar char={prefix} sizeStyle={sizeStyle} />}57
58        {digits.map((char, idx) =>59          /\d/.test(char) ? (60            <AnimatedDigit61              key={`${idx}-${number}`}62              target={parseInt(char)}63              delay={idx * 60}64              size={size}65              durationPerDigit={durationPerDigit}66              blur={blur}67              direction={direction}68              easing={easing}69              className={digitClassName}70            />71          ) : (72            <StaticChar key={idx} char={char} sizeStyle={sizeStyle} />73          ),74        )}75
76        {suffix && <StaticChar char={suffix} sizeStyle={sizeStyle} />}77      </div>78
79      {label && (80        <span className="text-muted-foreground text-sm tracking-wide">81          {label}82        </span>83      )}84    </div>85  );86};87
88// ---------------------------------------------------------89// Static Char (for prefix, suffix, separators)90// ---------------------------------------------------------91const StaticChar: React.FC<{92  char: string;93  sizeStyle: { fontSize: string; height: string };94}> = ({ char, sizeStyle }) => (95  <span96    className="font-light tabular-nums"97    style={{98      fontSize: sizeStyle.fontSize,99      lineHeight: sizeStyle.height,100    }}101  >102    {char}103  </span>104);105
106// ---------------------------------------------------------107// AnimatedDigit Component108// ---------------------------------------------------------109interface AnimatedDigitProps {110  target: number;111  delay?: number;112  size?: AnimatedSize;113  blur?: boolean;114  durationPerDigit?: number;115  direction?: "up" | "down";116  easing?: [number, number, number, number];117  className?: string;118}119
120export const AnimatedDigit: React.FC<AnimatedDigitProps> = ({121  target,122  delay = 0,123  size = "xl",124  blur = false,125  durationPerDigit = 0.6,126  direction = "up",127  easing = [0.22, 1, 0.36, 1],128  className,129}) => {130  const { height, fontSize, width } = sizeMap[size];131  const heightValue = parseFloat(height.replace("rem", ""));132
133  const digits = useMemo(() => Array.from({ length: 10 }, (_, i) => i), []);134
135  return (136    <div137      className={cn(138        "inline-block overflow-hidden text-center tabular-nums",139        blur && "bg-white/10 backdrop-blur-xs dark:bg-black/10",140      )}141      style={{ height, width, lineHeight: height }}142    >143      <motion.div144        initial={{ y: 0 }}145        animate={{146          y:147            direction === "up"148              ? `-${target * heightValue}rem`149              : `${target * heightValue}rem`,150        }}151        transition={{152          delay: delay / 1000,153          duration: durationPerDigit + target * 0.04,154          ease: easing,155        }}156      >157        {digits.map((digit) => (158          <div159            key={digit}160            className={cn("font-light select-none", className)}161            style={{ height, fontSize }}162          >163            {digit}164          </div>165        ))}166      </motion.div>167    </div>168  );169};

Update the import paths to match your project setup.

Basic Usage

1<AnimatedNumber number={1234567890} />

Advanced Usage

1<AnimatedNumber number={1234567890} />

Props

PropTypeRequiredDefaultDescription
numbernumberNoThe number to animate

Best Practices

  1. Clear Labels: Use descriptive and concise text reveal labels
  2. Consistent Icons: Use consistent icon styles throughout
  3. Logical Grouping: Group related text reveal items together
  4. Keyboard Navigation: Ensure all items are accessible via keyboard
  5. Loading States: Show loading indicators for dynamic content