WebberUI

Rolling Number

Odometer-style number wheels — when value changes, each digit springs vertically to its new value; added digits fade in and removed digits collapse.

Loading preview…
npx shadcn@latest add https://webberui.com/r/rolling-number.json

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

12345
0
<RollingNumber />

Installation

npx shadcn@latest add https://webberui.com/r/rolling-number.json

Or, once registries are configured in components.json, install it as @webberui/rolling-number.

Usage

import { RollingNumber } from "@/components/ui/rolling-number";

const [price, setPrice] = React.useState(1280);

<RollingNumber value={price} prefix="$" className="text-5xl font-bold" />

Just update value and every digit rolls to its new position. Font size, weight, and color are all controlled through className; internally the wheel height is computed in em, so it stays aligned at any font size.

With decimals and a suffix:

<RollingNumber value={98.6} decimals={1} suffix="%" />

Props

RollingNumber

PropTypeDefaultDescription
valuenumberThe number to display; when it changes, every digit rolls to its new value
decimalsnumber0Number of decimal places
prefixstring""Prefix before the number (for example $)
suffixstring""Suffix after the number (for example + or %)
localestring"en-US"Intl.NumberFormat locale, which decides the thousands and decimal separators
springConfigSpringOptions{ stiffness: 300, damping: 30, mass: 0.8 }Spring parameters for the wheels
classNamestringAppended to the outermost <span> className

How it works

  • How it differs from Animated Stats: Animated Stats is an integer count-up — a single MotionValue tweens from 0 to the target and the whole string is rewritten frame by frame. Rolling Number is odometer-style — each digit is its own vertical wheel (a column of 0–9 stacked vertically), and when value changes each wheel rolls to its own target digit. The former suits statistics that animate once on reveal; the latter suits live values that change repeatedly (prices, stock levels, counters)
  • Wheel mechanics: each digit column is 1em tall with overflow: hidden, containing 0–9 stacked vertically at 1em each; a spring drives translateY = -digit * 1em to roll to the target cell
  • Stagger: each digit is delayed by 0.03 seconds going from the lowest place upward (the ones digit rolls first), producing the linked feel of a mechanical gauge
  • Adding and removing digits: character keys are laid out by position counted from the right, so the ones place is always the same wheel. When digits are added, the new place (and the new thousands comma) fades in from the left and expands its width; when digits are removed, the old place collapses its width and fades out (AnimatePresence plus a width animation)
  • Separator characters: the thousands comma and the decimal point come from Intl.NumberFormat and are static characters that do not roll. tabular-nums is applied throughout, so digits are monospaced and do not jitter while rolling

Accessibility

  • When the user has "reduce motion" enabled at the system level, the wheels and digit transitions are skipped and the new value is shown as static text
  • The container carries an aria-label (the fully formatted value, including prefix and suffix) and the wheel layer is hidden from assistive technology (aria-hidden), so the screen reader announces only the current complete value and never reads the 0–9 inside the wheels

On this page