Animated Count
A number that springs from its previous value to a new target on every change. Drop it in next to anything that ticks — score, balance, count, percentage — and let the value settle instead of flashing.
0
Customize
Target
1234
Motion
smooth
Format
Installation
npx shadcn@latest add https://craftbits.dev/r/animated-count.jsonUsage
import { AnimatedCount } from "@craft-bits/core";
<AnimatedCount value={1234} />Pass format for currency, percentages, or any custom display:
import { SPRINGS } from "@craft-bits/core/motion";
<AnimatedCount
value={amount}
transition={SPRINGS.bouncy}
format={(v) => `$${v.toFixed(2)}`}
/>Understanding the component
- Motion-value driven. A
useMotionValueholds the current animated number. On everyvaluechange,animate(mv, value, transition)springs the motion-value toward the new target without remounting the DOM node. - Format runs every frame.
useTransform(mv, format)maps the raw number to a display string on each frame. The component mirrors that string into React state so the DOM re-renders as the spring settles. - Spring transition. Defaults to
SPRINGS.smooth. Swap toSPRINGS.bouncyfor celebratory counts orSPRINGS.snapfor crisp tick-ups. - Tabular numerics. The rendered span uses
font-variant-numeric: tabular-numsso digits don't shift width while ticking — the counter stays locked to a fixed grid. - Reduced motion. When
prefers-reduced-motion: reduceis set, the value snaps instantly to its new target — the spring is skipped entirely.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | required | The target value. Each change springs from the previous target. |
transition | Transition | SPRINGS.smooth | Framer transition (use a named spring from @craft-bits/core/motion). |
format | (v: number) => string | Math.round(v).toString() | Transform the raw motion-value into a display string. |
className | string | — | Merged onto the rendered motion.span. |
Accessibility
- The component renders a plain
<span>whose text content updates frame-by-frame. Screen readers will not announce intermediate values. - This component does not set
aria-live. The surrounding context owns announcement policy — wrap the count in an element witharia-live="polite"only when you actually want announcements. - Animation is fully disabled when
prefers-reduced-motion: reduceis set — the value snaps to its new target with no spring.
Credits
- Extracted from:
algoflashcards(src/lessons/primitives/viz/AnimatedCount.tsx).