> ## Documentation Index
> Fetch the complete documentation index at: https://api.fanvue.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# AnimatedNumber

> Animates a number when its value changes, either by rolling each digit like an odometer or by counting through the intermediate values.

```tsx theme={null}
import { AnimatedNumber } from "@fanvue/ui";
import type { AnimatedNumberProps, AnimatedNumberVariant } from "@fanvue/ui";
```

## Examples

## Default

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-animatednumber--default&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="280" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="AnimatedNumber — Default" />
</Frame>

```tsx theme={null}
import { AnimatedNumber } from "@fanvue/ui";

<AnimatedNumber value={1234} variant="count" durationMs={0} />
```

## Roll At Rest

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-animatednumber--roll-at-rest&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="280" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="AnimatedNumber — Roll At Rest" />
</Frame>

```tsx theme={null}
import { AnimatedNumber } from "@fanvue/ui";

const formatPrice = (value: number) =>
  `$${(Math.round(value) / 100).toLocaleString("en-US", {
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  })}`;

const GROSS = 12_804_673;

<AnimatedNumber value={GROSS} variant="roll" durationMs={0} format={formatPrice} />
```

## Comparison

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-animatednumber--comparison&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="280" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="AnimatedNumber — Comparison" />
</Frame>

```tsx theme={null}
import * as React from "react";
import { AnimatedNumber, Button } from "@fanvue/ui";

const formatPrice = (value: number) =>
  `$${(Math.round(value) / 100).toLocaleString("en-US", {
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  })}`;

const GROSS = 12_804_673;

const NET = 10_243_738;

function Example() {
  const [value, setValue] = React.useState(GROSS);

  return (
    <div className="flex w-96 flex-col items-start gap-6">
      <Button
        variant="secondary"
        size="40"
        onClick={() => setValue((current) => (current === GROSS ? NET : GROSS))}
      >
        Toggle net / gross
      </Button>

      <div className="flex flex-col gap-1">
        <span className="typography-description-12px-regular text-content-secondary">
          roll — headline figure
        </span>
        <span className="typography-header-heading-lg text-content-primary">
          <AnimatedNumber value={value} format={formatPrice} variant="roll" />
        </span>
      </div>

      <div className="flex flex-col gap-1">
        <span className="typography-description-12px-regular text-content-secondary">
          count — supporting figure
        </span>
        <span className="typography-body-default-16px-semibold text-content-primary">
          <AnimatedNumber value={value} format={formatPrice} variant="count" />
        </span>
      </div>
    </div>
  );
}
```

## Digit Count Change

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-animatednumber--digit-count-change&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="280" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="AnimatedNumber — Digit Count Change" />
</Frame>

```tsx theme={null}
import * as React from "react";
import { AnimatedNumber, Button } from "@fanvue/ui";

const formatPrice = (value: number) =>
  `$${(Math.round(value) / 100).toLocaleString("en-US", {
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  })}`;

function Example() {
  const [value, setValue] = React.useState(999_99);

  return (
    <div className="flex w-96 flex-col items-start gap-6">
      <Button
        variant="secondary"
        size="40"
        onClick={() => setValue((current) => (current === 999_99 ? 1_000_00 : 999_99))}
      >
        Cross the thousand
      </Button>
      <span className="typography-header-heading-lg text-content-primary">
        <AnimatedNumber value={value} format={formatPrice} variant="roll" />
      </span>
    </div>
  );
}
```

## Props

## AnimatedNumber

Animates a number when its value changes, either by rolling each digit like an odometer or by counting through the intermediate values.

Both variants collapse to an instant swap under `prefers-reduced-motion: reduce`.

The rolling variant stacks all ten digits in each column, so the visual is hidden from assistive technology and the formatted value is exposed once as text instead.

<ParamField path="value" type="number" required>
  The value to display. Animates whenever this changes.
</ParamField>

<ParamField path="durationMs" type="number" default="500">
  Animation duration in milliseconds.
</ParamField>

<ParamField path="format" type="((value: number) => string)" default="(value: number) => String(Math.round(value))">
  Turns the value into the displayed string. Receives interpolated values in the `count` variant, so it must handle non-integers.
</ParamField>

<ParamField path="variant" type="&#x22;roll&#x22; | &#x22;count&#x22;" default="count">
  Which animation to run.
</ParamField>

## Exported types

Also exported from `@fanvue/ui`: `AnimatedNumberProps`, `AnimatedNumberVariant`.

***

**Setup:** [Installation](/docs/ui/installation) · [Theming](/docs/ui/theming)
