Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Text

Free

Counting Number for React

A number that counts to its value through every number on the way, slowing as it lands. For React with Base UI, when a statistic should arrive with a little momentum.

Framework

Counting Number counts to its value through every number on the way, like a ticker: it races off, then slows as it closes in, so the last digits tick over one by one before it lands. Give it a new value and it counts on from wherever it is.

Use it for a statistic in a hero, a total on a dashboard, or a figure that grows as people watch. For digits that roll like an odometer, each in its own column, use Sliding Number; for a percentage, Progress Text. A small timer moves the count on every 30 ms and each number is written with Intl.NumberFormat, with no animation library; with reduced motion the value shows at once.

Preview width
48,215

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.

Usage

import { CountingNumber } from "@/components/ui/counting-number";
<CountingNumber value={48215} />

Demos

Format

grouping adds thousands separators, and decimals fixes the digits after the point, so 4.8 counts as 0.0, 0.1… rather than in whole numbers. The number is written in locale, en-US by default, so the server and the browser write it alike; pass your own for its separators, as de-DE writes 1.249,90.

Preview width

Change

Each new value counts on from the number showing, even mid-count, so a total that keeps changing never jumps. from is where the first count starts: here, the total itself, so it only counts the sales. duration is one count, 2000 ms by default; a short one suits a figure that changes often.

Preview width

From

from can be above the value, and it counts down. trigger="view" holds it at from until it scrolls into view.

Preview width

Accessibility

Screen readers read the value once, formatted (48,215), from a visually hidden copy, never the numbers on the way; the count itself is hidden from them. It doesn't announce changes by itself: wrap it in a live region (role="status") when a new value should be read out. The figures are tabular, so the number doesn't shift sideways as its digits change.

FAQ

How is it different from Sliding Number?

Counting Number shows every number on the way, written out as text, like a ticker. Sliding Number rolls each digit in its own column straight to its new value, like an odometer, and suits a value that changes by small steps.

Why does it slow down at the end?

The count is eased out: most of the distance goes by in the first moments and the rest slows into the value, so the eye lands on the number rather than chasing it. It never overshoots.

Will the text around it move as it counts?

The digits are tabular, all as wide as each other, so the number only grows when it gains a digit, which happens in the first few moments of a count. For a fixed width from the start, pass pad with the number of digits, which fills the empty places with zeros.

How do I add a currency sign or a unit?

Write it next to the number, as in $<CountingNumber value={128450} /> or <CountingNumber value={42} />%. It stays still while the number counts.

Can it count again?

Give it a new key and React renders it fresh, so it counts again from from. A new value alone counts on from where it is.

What does it show without JavaScript?

The value itself. With JavaScript, the page renders the number at from and counts once the page is running.

What happens with reduced motion?

The value shows at once, without counting, and onComplete is called straight away.

API Reference

CountingNumber

<span> data-slot="counting-number"

PropType
valuenumber

The number. Each new value counts on from wherever the number is.

decimals?number

How many digits after the decimal point, always shown. @default 0.

Default
0
duration?number

How long a count takes from start to finish, in milliseconds. @default 2000.

Default
2000
from?number

Where the first count starts. @default 0.

Default
0
grouping?boolean

Thousands separators (12,480). @default false.

Default
false
locale?string

The locale the number is written in (its digits, decimal point and grouping). Fixed by default, so the server and the browser write it the same way.

Default
"en-US"
onComplete?() → void

Called each time a count reaches its value.

Returns
void
pad?number

At least this many digits before the decimal point (05). @default 1.

Default
1
trigger?"mount" | "view"

When the count starts: as soon as the page shows it, or once it scrolls into view.

Type
"mount" | "view"
Default
"mount"
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

All 97 text effects