Skip to content
Signature

MotionNumber

Animated numeric readout — a smooth count-up, or an odometer that rolls each digit reel into place (reduced-motion aware).

● LIVE
Throughput
847req/s
Signal
99.9%
Credits
12,480
npx shadcn@latest add https://okibi.cndr.dev/r/motion-number.json

Installation

Install the theme first, then add the component:

npx shadcn@latest add https://okibi.cndr.dev/r/motion-number.json

Install the required dependencies:

npm install motion

Copy the source from the Code tab above into components/ui/motion-number.tsx. It uses the cn helper and the okibi theme tokens — install the theme first if you haven't.

Usage

import { MotionNumber } from "@/components/ui/motion-number"

<MotionNumber value={1024} variant="odometer" digits={4} />

Examples

Formatted

Roll to fixed decimals, or pass a format function for units, percentages, and currency.

● LIVE
decimals
99.94
format
87%
integer
847

Odometer

Set variant="odometer" to roll each digit on its own vertical 0–9 reel into place with a hard settle, cascading right-to-left — mechanical digit travel rather than the default smooth value interpolation. Pad to a fixed width with digits.

● LIVE
01280

API Reference

MotionNumber

PropTypeDefaultDescription
valuenumberTarget value to animate to.
variant"count" | "odometer""count"Interpolate the text, or roll digit reels.
durationnumber0.9Roll duration in seconds (count).
digitsnumberMinimum zero-padded integer digits (odometer).
decimalsnumberFixed decimal places.
format(n: number) => stringCustom formatter; overrides decimals (count).

Accessibility

  • Both variants expose the resolved final value to assistive tech via an sr-only aria-live='polite' node, so screen readers read the settled number, not the interpolation.
  • The animating count text and every odometer digit reel are decorative and marked aria-hidden.
  • Honors prefers-reduced-motion: the final value renders instantly with no count-up or reel roll in either variant.
  • Renders tabular-nums so the readout never shifts width as digits change.
  • For values that update on a fast interval, reconsider the polite live region so a screen reader is not flooded with announcements.

Guidelines

Do

  • Use count for smooth metric count-ups and odometer for a mechanical split-flap roll.
  • Set digits on odometer for a fixed-width, zero-padded readout.
  • Drive color from the consumer with classes like text-signal-spark; the component ships no color of its own.

Don't

  • Do not point a polite MotionNumber at a value that ticks many times a second without dampening the live region.
  • Do not expect digits to zero-pad the count variant — padding applies to odometer only.
  • Do not wrap it in your own aria-live region; it already exposes the resolved value to screen readers.

On this page