# Number Roll
Jun 2026 · [Source](https://github.com/assistant-ui/assistant-ui/blob/main/packages/ui/src/components/assistant-ui/number-roll.tsx)
Agent UIs are full of numbers that refuse to sit still. Token counts climb, costs tick upward, progress moves. Swap the text and the number teleports; the eye catches that something changed but not how, or which way. An odometer fixes both. Roll the digits and you read the new value and the direction of travel in the same glance.

## The digit wheel

Each digit is a strip of ten glyphs stacked in a single character cell. Their positions all derive from one number, a registered CSS custom property, pushed through `mod()` arithmetic so the strip wraps: 9 rolls forward into 0 instead of rewinding through the whole wheel. The transition animates the property itself. The browser tweens one number, every glyph computes its own offset each frame, and a clamp parks off-screen glyphs one line-height away. There is no JavaScript animation loop; change the value and CSS does the rest, eased with the same strong ease-out this site uses for its own entrances.

## Formatting is the hard part

A bare integer is easy. Real numbers arrive as $1,024.50, 1.1K, 98%, or 1.024,50 depending on locale. Everything goes through `Intl.NumberFormat.formatToParts`, so digits know they are digits, and everything else (currency signs, group separators, compact suffixes) is a symbol that slides and fades instead of spinning. Compact notation, currencies, percentages, any locale: they all roll.

## 999 rolls into 1,000

The tricky change is not 5 to 6; it is 999 to 1,000, when the number grows a digit and a separator at once. Integer digits are keyed right to left, so the ones place is always the ones place no matter how long the number gets. Surviving places keep their identity and roll; only the new leading characters enter, fading in through `@starting-style` while the column width animates, so the neighbors glide apart instead of jumping.

## Direction

By default the wheel takes the shortest path and infers direction from the change. Sometimes you know better: a request counter only ever climbs, a countdown only ever falls. The `trend` prop forces the roll direction, and entering and exiting symbols slide with it, so the whole number reads as moving one way.

## What the page never sees

The wheel is decoration and stays in its lane. A visually hidden span carries the real formatted value for screen readers; the strip glyphs render through CSS `content`, so copy and find-in-page never meet them. On the server, and in browsers without CSS `mod()` support, the component renders the plain formatted string and upgrades in place.

## Usage

One `value` prop plus `Intl.NumberFormat` options. It ships as a standalone component in assistant-ui's registry; the [full reference](https://www.assistant-ui.com/docs/ui/number-roll) lives in the docs.

```tsx
import { NumberRoll } from "@/components/assistant-ui/number-roll";

export function TokenCounter({ tokens }: { tokens: number }) {
  return <NumberRoll value={tokens} format={{ notation: "compact" }} trend="up" />;
}
```

## A span that rolls

No dependencies, no animation loop, no layout thrash. Server-rendered text that knows which way the number went.