Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Text

Pro

Progress Text for React

A percentage that winds up to its value like an odometer, its reels counting through every value on the way. For React with Base UI, when a figure says how far something has got.

Framework

Progress Text shows a percentage on a row of reels that wind up from zero to its value like an odometer: the last digit spins, and each digit before it rolls over as the one after it passes nine, so the number counts through every value on the way and settles exactly on its own. Each new value winds on from where the reels are.

Use it for how far something has got: uptime, a goal that's been funded, an upload, a course completed. For any number counted out as text, use Counting Number; for digits that roll straight to a new value, Sliding Number. There's no timer: every reel follows one number that CSS eases, through a transition and keyframes the install adds to your stylesheet, with no animation library. With reduced motion the value shows at once.

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Usage

import { ProgressText } from "@/components/ui/progress-text";
<ProgressText value={98} />

Demos

Format

decimals fixes the digits after the point (99.98%), and max is the value that makes 100%: here 2,936 out of 4,000 is 73.4%. The percentage is written in locale, en-US by default, so the server and the browser write it alike; pass your own for its digits and where its percent sign goes, as Turkish writes %64.

Preview width

Upload

Each new value winds the reels on from where they are, so a figure that changes often moves smoothly between its steps. trigger="mount" winds it up as soon as the page shows it rather than once it scrolls into view, and duration is one wind, 1400 ms by default. Here it sits beside a Progress bar.

Preview width

Accessibility

Screen readers read the percentage once, formatted (98%), from a visually hidden copy; the reels are hidden from them. It doesn't announce changes by itself: next to a progress bar, the bar is what assistive technology follows, so give it a label. Every reel is as wide as a digit, so the number never shifts as the reels turn. With reduced motion the value shows at once.

FAQ

How do the reels count?

Like a car's odometer. All the reels follow one number as it eases to the value: the last one turns with it, and every other one moves only while the digits after it pass from nine to zero, so 09% rolls over to 10% exactly as it would on a dial.

How is it different from Sliding Number?

Sliding Number moves each digit straight to its new value, so a column rolls at most once round. Progress Text counts through every value, its last reel spinning as the number climbs, and writes a percentage.

When does it wind up?

Once, when at least a quarter of it is in view; until then it shows zeros. Pass trigger="mount" to wind it up as soon as the page shows it. Give it a new key to wind it up from zero again.

Can it go over 100%?

Yes: a value above max shows more than 100%, and the reels gain a digit. Below zero it shows 0%.

Which browsers does it need?

Any current one. The reels use CSS mod() and round() and a registered custom property, which every evergreen browser has supported since 2024.

What does it show without JavaScript?

The value, without winding: nothing would tell it the text is in view, so it doesn't wait.

What happens with reduced motion?

The value shows at once, and a new value replaces it in place.

API Reference

ProgressText

<span> data-slot="progress-text"

PropType
valuenumber

How far along, out of max. Each change winds the reels on to it.

decimals?number

How many digits after the decimal point, always shown (73.4%). @default 0.

Default
0
duration?number

How long the reels take to wind to a new value, in milliseconds. @default 1400.

locale?string

The locale the percentage is written in: its digits, its decimal point and where the percent sign goes. Fixed by default, so the server and the browser write it the same way.

Default
"en-US"
max?number

The value that makes 100%. @default 100.

Default
100
trigger?"mount" | "view"

When it winds up from zero: as soon as the page shows it, or once it scrolls into view.

Type
"mount" | "view"
Default
"view"
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.

AttributeType
data-view?string

"in" when visible is true, otherwise "out".

CSS variableType
--progress-text-durationstring

How long the reels take to wind to a new value (1400ms).

All 97 text effects