my/ui

Command Palette

Search for a command to run...

All components

Animated Number

data-display

Number component with smooth counting animations and customizable formatting

responsive · 380px

Install

Same command in any shadcn project — React (Vite/CRA), Next.js, Remix, Astro, and more:

$npx shadcn@latest add https://your-domain/r/animated-number.json

Usage

"use client";

import { useState } from "react";
import { AnimatedNumber } from "@/registry/cult-ui/animated-number";

export default function Demo() {
  const [value, setValue] = useState(1234);

  const targets = [0, 999, 1234, 42000, 1000000];

  return (
    <div className="w-full max-w-sm mx-auto flex flex-col items-center gap-6 p-8">
      <div className="text-5xl font-bold text-white tabular-nums">
        <AnimatedNumber
          value={value}
          mass={0.8}
          stiffness={75}
          damping={15}
          precision={0}
          format={(n) => n.toLocaleString()}
        />
      </div>
      <div className="flex flex-wrap gap-2 justify-center">
        {targets.map((t) => (
          <button
            key={t}
            onClick={() => setValue(t)}
            className="px-3 py-1.5 text-sm rounded-lg bg-neutral-800 text-white hover:bg-neutral-700 transition-colors"
          >
            {t.toLocaleString()}
          </button>
        ))}
      </div>
    </div>
  );
}

Component source

"use client"

import { useEffect } from "react"
import { motion, MotionValue, useSpring, useTransform } from "motion/react"

interface AnimatedNumberProps {
  value: number
  mass?: number
  stiffness?: number
  damping?: number
  precision?: number
  format?: (value: number) => string
  onAnimationStart?: () => void
  onAnimationComplete?: () => void
}

export function AnimatedNumber({
  value,
  mass = 0.8,
  stiffness = 75,
  damping = 15,
  precision = 0,
  format = (num) => num.toLocaleString(),
  onAnimationStart,
  onAnimationComplete,
}: AnimatedNumberProps) {
  const spring = useSpring(value, { mass, stiffness, damping })
  const display: MotionValue<string> = useTransform(spring, (current) =>
    format(parseFloat(current.toFixed(precision)))
  )

  useEffect(() => {
    spring.set(value)
    if (onAnimationStart) onAnimationStart()
    const unsubscribe = spring.on("change", () => {
      if (spring.get() === value && onAnimationComplete) onAnimationComplete()
    })
    return () => unsubscribe()
  }, [spring, value, onAnimationStart, onAnimationComplete])

  return <motion.span>{display}</motion.span>
}

Dependencies

motion

Source: Cult UI