Number Flow
Animates a number by rolling each digit to its new value.
Anatomy
<NumberFlow.Root>
<NumberFlow.Segments />
<NumberFlow.HiddenValueText />
</NumberFlow.Root>
Segments renders one Digit per rolling digit and one Symbol per separator, currency sign, or literal. Each Digit
renders a DigitTrack that holds a strip of DigitCells, and the strip is translated so the right cell lands in view.
Pass a render function to Segments to compose the parts yourself:
<NumberFlow.Root>
<NumberFlow.Segments>
{(segment) =>
segment.kind === 'digit' ? (
<NumberFlow.Digit segment={segment}>
<NumberFlow.DigitTrack segment={segment} />
</NumberFlow.Digit>
) : (
<NumberFlow.Symbol segment={segment} />
)
}
</NumberFlow.Segments>
<NumberFlow.HiddenValueText />
</NumberFlow.Root>
Examples
Basic
Pass a value and the number rolls to it whenever it changes. Use defaultValue instead to let the component own the
value.
Formatting
formatOptions goes straight to Intl.NumberFormat, so currency, percent, compact notation, and precision all work as
they would in Intl. Only the digits roll; signs, separators, and currency symbols swap in place.
Pricing
Switching between two prices is a common use. Set trend so every digit rolls in the direction the price moved.
Trend
The root carries data-trend (up, down, or none) for the whole roll, so the color can follow the direction of the
last change.
Continuous
By default each digit takes its shortest path, so 9 to 0 rolls forward one step. Set continuous to spin through
every value in between instead, which makes a jump from 120 to 245 read as counting rather than a cut.
Timing
spinTiming controls the digit roll and transformTiming controls digits entering and leaving. Both take CSS
duration and easing values. Set stagger to delay each digit behind the one before it.
Locale
The number follows the locale from LocaleProvider, or the locale prop. Non-Latin numeral systems roll through their
own glyphs, so ar-EG rolls through ٠ to ٩.
Prefix and Suffix
Use prefix and suffix for static text around the number. They never roll, and they are included in the accessible
label.
Composition
Pass a function as the child of Segments to render each segment yourself. Each digit segment carries its decimal
place (0 is the ones column, negative values are fraction digits), and each symbol carries its Intl type, so you
can style the cents or the currency sign on their own. Segments keys every segment for you, so digits keep rolling
when the number gains or loses a digit.
Live Announcements
Set live to announce changes to screen readers. The value is announced through HiddenValueText once the roll
settles, so a fast counter is read once rather than for every intermediate value.
Root Provider
Use the useNumberFlow hook with RootProvider to read the value and call setValue from outside the component.
Guides
Styling
The component sets the structural styles inline: each digit is clipped to one line and the track is a vertical strip of
1lh cells. What is left is typography and color.
[data-number-flow-root] {
font-variant-numeric: tabular-nums;
}
[data-number-flow-symbol] {
white-space: pre;
}
[data-number-flow-digit] {
vertical-align: top;
}
Use a font with tabular figures. Proportional digits change width as they roll, which makes the number jitter.
The root exposes data-state (idle or rolling), data-trend, and data-reduced-motion. Digits carry data-place,
and symbols carry data-type with their Intl.NumberFormat part type, such as currency or group.
Accessibility
By default the root has role="img" and an aria-label with the formatted value, so assistive technology reads the
number instead of the strip of cells. HiddenValueText is visually hidden and stays silent.
With live, the root drops its role and HiddenValueText becomes a polite status region that holds the settled value.
Render HiddenValueText whenever you set live; without it, nothing is announced.
When the user prefers reduced motion, the number snaps to its new value. Set respectMotionPreference to false to
roll anyway.
API Reference
Props
Root
Renders a <span> element.
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
continuous | false | booleanWhether digits spin through every intermediate value instead of taking the nearest path. |
defaultValue | 0 | numberThe initial value to render when uncontrolled. |
formatOptions | NumberFormatOptionsThe options to pass to the `Intl.NumberFormat` constructor. | |
id | stringThe unique identifier of the machine. | |
ids | Partial<{ root: string; valueText: string }>The ids of the elements in the number-flow. Useful for composition. | |
live | false | booleanWhether the value is announced to assistive technology as it changes. When `false`, the rendered value is still readable, but changes are not announced. |
locale | 'en-US' | stringThe current locale. Based on the BCP 47 definition. |
onAnimationComplete | (details: AnimationDetails) => voidCallback fired when the roll animation settles. | |
onAnimationStart | (details: AnimationDetails) => voidCallback fired when a value change starts a roll animation. | |
onValueChange | (details: ValueChangeDetails) => voidCallback fired when the value changes. | |
prefix | stringA static string rendered before the formatted value. Does not animate. | |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | RenderFn<EmptyState>Render the part as a custom element, combining their props and behavior. Pass an element to have the part's props merged into it, or a function to control the merge yourself and read the part's state. | |
respectMotionPreference | true | booleanWhether to suppress the roll animation when the user prefers reduced motion. |
spinTiming | { duration: '900ms', easing: 'cubic-bezier(0.4, 0, 0.2, 1)' } | TimingOptionsThe timing of the per-digit roll transition. |
stagger | undefined | stringA CSS `<time>` delay applied between adjacent digits, e.g. `"25ms"`. |
suffix | stringA static string rendered after the formatted value. Does not animate. | |
transformTiming | { duration: '500ms', easing: 'cubic-bezier(0.4, 0, 0.2, 1)' } | TimingOptionsThe timing of layout transitions - segments entering/exiting. |
trend | false | TrendControls the roll direction of each digit. - `false` (default): each digit takes its own shortest path (`9 -> 0` rolls forward one step). - `true`: every digit rolls in the direction of the overall value change. - `1` / `-1`: force every digit to roll up or down, regardless of the value change. |
value | numberThe controlled numeric value to render. |
| Attribute | Description |
|---|---|
[data-number-flow-root] | <uid> |
[data-state] | "rolling" | "idle" |
[data-trend] | |
[data-reduced-motion] |
| CSS Variable | Description |
|---|---|
--spin-duration | The spin duration value for the root |
--spin-easing | The spin easing value for the root |
--transform-duration | The transform duration value for the root |
--transform-easing | The transform easing value for the root |
--stagger | The stagger value for the root |
--place-index | The place index value for the root |
DigitCell
Renders a <span> element.
| Prop | Default | Type |
|---|---|---|
cell | DigitCell | |
segment | DigitSegment | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | RenderFn<EmptyState>Render the part as a custom element, combining their props and behavior. Pass an element to have the part's props merged into it, or a function to control the merge yourself and read the part's state. |
| Attribute | Description |
|---|---|
[data-number-flow-digit-cell] | <uid> |
[data-place] | |
[data-digit] |
Digit
Renders a <span> element.
| Prop | Default | Type |
|---|---|---|
segment | DigitSegment | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | RenderFn<EmptyState>Render the part as a custom element, combining their props and behavior. Pass an element to have the part's props merged into it, or a function to control the merge yourself and read the part's state. |
| Attribute | Description |
|---|---|
[data-number-flow-digit] | <uid> |
[data-place] |
DigitTrack
Renders a <span> element.
| Prop | Default | Type |
|---|---|---|
segment | DigitSegment | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | RenderFn<EmptyState>Render the part as a custom element, combining their props and behavior. Pass an element to have the part's props merged into it, or a function to control the merge yourself and read the part's state. |
| Attribute | Description |
|---|---|
[data-number-flow-digit-track] | <uid> |
[data-place] |
RootProvider
Renders a <span> element.
| Prop | Default | Type |
|---|---|---|
value | UseNumberFlowReturn | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | RenderFn<EmptyState>Render the part as a custom element, combining their props and behavior. Pass an element to have the part's props merged into it, or a function to control the merge yourself and read the part's state. |
Symbol
Renders a <span> element.
| Prop | Default | Type |
|---|---|---|
segment | SymbolSegment | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | RenderFn<EmptyState>Render the part as a custom element, combining their props and behavior. Pass an element to have the part's props merged into it, or a function to control the merge yourself and read the part's state. |
| Attribute | Description |
|---|---|
[data-number-flow-symbol] | <uid> |
[data-type] | The type of the item |
HiddenValueText
Renders a <span> element.
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | RenderFn<EmptyState>Render the part as a custom element, combining their props and behavior. Pass an element to have the part's props merged into it, or a function to control the merge yourself and read the part's state. |