Rolling Number
betaA 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
step
currency
percent
function
in text
1,284 people visited this week, up from 1,102 last week.
Install
1. Add the registry to
components.json, once per app. Without it, the shadcn CLI looks up@txbrendanin a public list that txbrendan/ui does not control.npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.
npx shadcn@latest add @txbrendan/theme3. 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
| Prop | Type | Default | What it does |
|---|---|---|---|
| value | number | - | Required. The number to show. When it changes, each digit rolls to its new value. |
| format | Intl.NumberFormatOptions | ((value: number) => string) | { maximumFractionDigits: 0 } | How the number prints. Options work from a server component. A function works from a client component only. |
| locale | string | "en-US" | The locale for format options. Fixed, so server and browser print the same text. |
| className | string | - | Extra classes, like text size and weight. Later classes win. |
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)} />