NumberFlow

 for 
43,110

A component to transition, format, and localize numbers. Dependency-free. Accessible. Customizable.

Basic usage

// Basic usage
import NumberFlow from '@number-flow/react'

<NumberFlow value={123} />

<NumberFlow> will automatically transition when the value prop changes.


Props

format: Intl.NumberFormatOptions

The format options passed to the internal Intl.NumberFormat instance.

<NumberFlow format={{ notation: 'compact' }} value={value} />

locales: Intl.LocalesArgument

The locales passed to the internal Intl.NumberFormat instance.

prefix: string, suffix: string

A custom prefix or suffix for the number.

$3/moClick anywhere to change numbers
<NumberFlow
	value={value}
	format={{ style: 'currency', currency: 'USD', trailingZeroDisplay: 'stripIfInteger' }}
	suffix="/mo"
/>

Timings

There are three props to customize the animation timings. Each accept an EffectTiming object:

124.23Click anywhere to change numbers
<NumberFlow
	// Used for layout-related transforms:
	transformTiming={{ duration: 750, easing: 'linear(...)' }}
	// Used for the digit spin animations.
	// Will fall back to `transformTiming` if unset:
	spinTiming={{ duration: 750, easing: 'linear(...)' }}
	// Used for fading in/out characters:
	opacityTiming={{ duration: 350, easing: 'ease-out' }}
/>

For spring-based easings, I’d recommend Kevin Grajeda’s generator or easing.dev.

trend: number(oldValue: number, value: number) => numberDefault: (oldValue, value) => Math.sign(value - oldValue)

Controls the direction of the digits. If trend is or returns

  • +1: the digits always go up.
  • 0: each digit goes up if it increases and down if it decreases. This can be useful if you want to animate number changes without conveying an overall trend (example).
  • -1: The digits always go down.
20Click anywhere to change numbers

continuous: booleanDefault: false

If set, number transitions will appear to pass through in-between numbers. Has no effect if trend is 0.

120
Click anywhere to change numbers

isolate: booleanDefault: false

If isolate is set, <NumberFlow>’s transitions are isolated from any other layout changes that may occur in the same update. Has no effect when inside a <NumberFlowGroup>.

42%
Click anywhere to change numbers

animated: booleanDefault: true

Can be set to false to disable all animations and finish any current ones. See the input example for a usage scenario.

digits: Record<number, { max?: number }>

Configure digits based on their position in the number (i.e. for 342.5, the positions are: 324120.5-1). This can be helpful for time-related displays, to ensure e.g. 59 -> 00. See the countdown example for a demo.

digits is not currently reactive to save on bundle size. If you need it to be reactive, please submit a feature request.

willChange: booleanDefault: false

If set, NumberFlow applies will-change properties to relevant elements. This can be useful if:

  • Your number is guaranteed to change frequently
  • You experience text misalignment when a transition completes

Note that “excessive use of will-change will result in excessive memory use” (source: MDN).

respectMotionPreference: booleanDefault: true

Can be set to false to animate regardless of the user’s reduced motion preference.

onAnimationsStart: (e: CustomEvent) => void

Triggered when update animations start.

Not to be confused with the built-in onAnimationStart, which would trigger for animations on the <NumberFlow> element itself.

onAnimationsFinish: (e: CustomEvent) => void

Triggered when update animations finish.


Styling

NumberFlow uses a custom element under the hood, and exposes parts for styling purposes:

$3/moClick anywhere to change numbers

You can use your browser’s inspector to see which part attributes are available to style. Note that changing the font-size of digits is difficult due to the CSS techniques NumberFlow uses.

::part styles may cause a flash of unstyled content in old browsers.

See workaround You can use feature detection to apply ::part styles only to browsers that support Declarative Shadow DOM (DSD). Add the following snippet to your <head>:

<script>
	if (
		HTMLTemplateElement.prototype.hasOwnProperty('shadowRootMode') ||
		HTMLTemplateElement.prototype.hasOwnProperty('shadowRoot') // old Chrome/Edge
	)
		document.documentElement.setAttribute('data-supports-dsd', '')
</script>

Then ensure your ::part styles use it:

:root[data-supports-dsd] number-flow-react::part(suffix) {
	font-size: 0.75rem;
}

If you’re using Tailwind, you can do this with a custom variant:

// tailwind.config.js
import plugin from 'tailwindcss/plugin'

export default {
	// ...
	plugins: [
		plugin(({ matchVariant }) => {
			matchVariant('part', (p) => `:root[data-supports-dsd] &::part(${p})`)
		})
	]
}
<NumberFlow className="part-[suffix]:text-xs" />

There’s also some CSS properties you can use to style the component:

--number-flow-mask-[height|width]: <length>Default: .25em | .5em

These adjust the height and width of the gradient fade-out masks at the edges of the number. --number-flow-mask-height also gets used as the top and bottom padding for the number.

--number-flow-char-height: <length>Default: 1em

Sets the height of each character. This can be used to adjust the spacing between digits during spin animations.

font-variant-numeric: tabular-nums

Ensures all numbers are the same width, which can prevent digits from shifting during transitions. See MDN for more information.


Grouping

If you’re using multiple <NumberFlow> components on one line, you can wrap them in a <NumberFlowGroup> to properly sync their transitions:

$124.23+5.64%
Click anywhere to change numbers

<NumberFlowGroup> doesn’t render an element or accept any props.


Hooks

useCanAnimate(opts?: { respectMotionPreference?: boolean }): boolean

Returns true if NumberFlow can animate, i.e. the browser supports the required features and (optionally) the user is okay with motion. See the Motion for React example for a usage scenario.


Limitations

  • Scientific and engineering notations aren’t supported.
  • Backgrounds and borders on <NumberFlow> won’t scale smoothly during transitions. I’d recommend using Motion for React for these, as it’s more composable than any built-in solution could be. See the Motion for React layout animations example.