Animated Number
A number display that tweens from its previous value to a new one on every change — the dashboard stat-card counter.
- Status
- Since
0.1.0- Accessibility pattern
- decorative text (aria-hidden)
Last updated
Install
Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Animated Number before installation. Retain the digest printed by this command.
pnpm exec vegastack-design verify animated-numberInstall with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.
pnpm dlx shadcn@4.21.0 add @vegastack/animated-numberRun the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.
The same command installs the registry items it composes: @vegastack/use-media-query.
Usage
import { AnimatedNumber } from "@/components/ui/animated-number";
<AnimatedNumber value={revenue} />;Renders the exact value statically on mount — no count-up flash, no hydration mismatch. Every
LATER value change tweens the displayed number from whatever is currently shown to the new
value, over the duration motion token.
Examples
Currency and compact formats
format takes full Intl.NumberFormatOptions — currency, percent, compact notation, custom
grouping. Every intermediate frame of the tween is formatted through the same
Intl.NumberFormat(locale, format) instance as the settled value, so the $ symbol, cent
grouping, or compact suffix ("12.4K") stay correct throughout the animation, not just at rest.
Duration
duration is a motion-token name ('fast' | 'base' | 'slow', default 'base') — resolved from
the live --duration-fast/base/slow CSS custom property at animation start, never a hardcoded
millisecond value. Easing reads --motion-ease-standard the same way. Overriding either token at
runtime (e.g. scoping --duration-base on a container) retunes the tween without a prop.
Interruption
Changing value again before a tween finishes retargets it smoothly from wherever the number
currently sits — it never snaps back to an earlier value first.
// Rapid updates (e.g. a live ticker) each interrupt the last, always converging on
// the LATEST value with one continuous motion, not a stutter per update.
<AnimatedNumber
value={livePrice}
format={{ style: "currency", currency: "USD" }}
/>Playground
Pick a different value preset to watch the tween, try the format and duration options, then copy the generated JSX.
<AnimatedNumber value={1204} />API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
value* | number | — | The number to display. On mount it renders statically (no animation). On every later change, the displayed value tweens from whatever is currently shown to this new value. Changing it again mid-tween interrupts the current animation and retargets from the in-flight value — it never snaps back to an earlier value first. |
duration | "base" | "fast" | "slow" | 'base' | Tween duration, as a motion-token name (never a raw millisecond value) — resolved from the
live --duration-{fast,base,slow} CSS custom property at animation start. |
format | Intl.NumberFormatOptions | — | Intl.NumberFormatOptions used to format both the settled value and every animated
intermediate frame — e.g. { style: 'currency', currency: 'USD' } or
{ notation: 'compact' }. Omit for plain locale-grouped digits. |
locale | string | string[] | — | BCP-47 locale(s) for Intl.NumberFormat. Defaults to the runtime locale. |
Data attributes and CSS variables on AnimatedNumber
| Attribute | Values |
|---|---|
data-slot | "animated-number" | "animated-number-live" | "animated-number-value" |
Accessibility
- The ticking visual text is
aria-hidden— a rapid stream of announcements while a number climbs would be noise, not useful information. - A visually-hidden
role="status"/aria-live="polite"region announces only the settled value, once per tween completion — never the intermediate frames. prefers-reduced-motion: reducerenders every value change instantly, with no tween at all — the same SSR-safematchMediahook used byTruncatedText's no-hover detection andMessageScroller's reduced-motion handling.- Digits are the regular face with
tabular-nums— the same face asStatValue— so the layout never jitters horizontally as the character count changes mid-animation (e.g.99→100). Mono is for codes and IDs, not counts. - Server-safe: the initial render (server AND client) always shows the static final value — there is no hydration mismatch to reconcile.
| Contract | States tested |
|---|---|
| Behaviour | default, empty |
| Accessibility | live, status-announcement, semantic-html |
| Visual | default, empty |