Skip to content
txbrendan/ui

Rolling Number

beta

A number whose digits roll to each new value, each one the way the whole value moved, so a value that changes during a scrub or a step reads as one number moving.

Preview

scrub

1,284

step

19 seats taken

currency

$4,173.00

percent

6.4%

function

21 min

in text

1,284 people visited this week, up from 1,102 last week.

Install

  1. 1. Add the registry to components.json, once per app. Without it, the shadcn CLI looks up @txbrendan in a public list that txbrendan/ui does not control.

    npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"
  2. 2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.

    npx shadcn@latest add @txbrendan/theme
  3. 3. Add the component.

    npx shadcn@latest add @txbrendan/rolling-number

Use it for

  • A number that changes while a hover, scrub, or step is still going: a chart reading, a calendar tip, a stepper, a live count.
  • Pass value. Pass format to print it another way, like { style: "currency", currency: "USD" }.
  • Set its size and weight with className. It uses tabular digits, so the width holds while it rolls. A digit it gains or drops folds in or out, so the words beside it glide.

Avoid it for

  • A number that never changes. Print it as text.
  • A value people need to read every step of, like a countdown. It shows only the end of each roll.
  • Long text with digits in it, like a phone number. Only numbers roll.

Props

  • value

    Required. The number to show. When it changes, each digit rolls to its new value.

    number

  • format

    How the number prints. Options work from a server component. A function works from a client component only.

    Intl.NumberFormatOptions | ((value: number) => string) · default { maximumFractionDigits: 0 }

  • locale

    The locale for format options. Fixed, so server and browser print the same text.

    string · default "en-US"

  • className

    Extra classes, like text size and weight. Later classes win.

    string

Accessibility

  • Screen readers hear the final value only, as plain text. The rolling digits are hidden from them.
  • It is not a live region. If a change must be announced, say it elsewhere.
  • Each digit rolls over --motion-duration-slow on --motion-ease-spring. Reduced motion jumps straight to the new value, and so does motion off.

Examples

A count

<RollingNumber value={1284} />

Money

<RollingNumber value={4173.5} format={{ style: "currency", currency: "USD" }} className="text-2xl font-semibold" />

Following a slider

const [value, setValue] = useState(40);

<RollingNumber value={value / 100} format={{ style: "percent" }} />
<Slider aria-label="Share" value={value} onValueChange={(next) => setValue(next as number)} />