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.
<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
<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
trend: number(oldValue: number, value: number) => number
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.
continuous: boolean
If set, number transitions will appear to pass through in-between numbers. Has no effect if trend
is 0
.
isolate: boolean
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>
animated: boolean
Can be set to false
to disable all animations and finish any current ones.
See the
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
digits
is not currently reactive to save on bundle size.
If you need it to be reactive, please submit a
willChange: boolean
If set, NumberFlow applies will-change
properties
- 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:
respectMotionPreference: boolean
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
number-flow-react::part(suffix) {
margin-left: .0625em;
font-weight: normal;
font-size: 0.75em;
color: var(--muted);
}
You can use your browser’s inspector to see which part
attributesfont-size
of digits is difficult
due to the CSS techniques NumberFlow uses.
::part
styles may cause a flash of unstyled content in
See workaround
You can use feature detection
to apply ::part
styles only to browsers that support <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>
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>
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
Grouping
If you’re using multiple <NumberFlow>
components on one line, you can wrap
them in a <NumberFlowGroup>
to properly sync their transitions:
import NumberFlow, { NumberFlowGroup } from '@number-flow/react'
import clsx from 'clsx/lite'
type Props = {
value: number
diff: number
}
export default function PriceWithDiff({ value, diff }: Props) {
return (
<NumberFlowGroup>
<div
style={{ '--number-flow-char-height': '0.85em' }}
className="flex items-center gap-4 font-semibold"
>
<NumberFlow
value={value}
format={{ style: 'currency', currency: 'USD' }}
className="~text-2xl/4xl"
/>
<NumberFlow
value={diff}
format={{ style: 'percent', maximumFractionDigits: 2, signDisplay: 'always' }}
className={clsx(
'~text-lg/2xl transition-colors duration-300',
diff < 0 ? 'text-red-500' : 'text-emerald-500'
)}
/>
</div>
</NumberFlowGroup>
)
}
<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
Limitations
andScientific notations aren’t supported.engineering - Backgrounds and borders on
<NumberFlow>
won’t scale smoothly during transitions. I’d recommend using for these, as it’s more composable than any built-in solution could be. See the Motion for React layout animationsMotion for React .example