Basic usage
<!-- Basic usage -->
<script setup>
import NumberFlow from '@number-flow/vue'
</script>
<template>
<NumberFlow :value="123" />
</template>
<NumberFlow>
will automatically transition when the value
prop changes.
Props
format: Intl.NumberFormatOptions
Formatting options for the number; see MDN for
<NumberFlow :format="{ notation: 'compact' }" :value />
locales: Intl.LocalesArgument
The locale(s) for the number; see MDN for
prefix: string, suffix: string
A custom prefix or suffix for the number.
<NumberFlow
: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
:transformTiming="{
// Used for layout-related transforms:
duration: 750, easing: 'linear(...)'
}"
:spinTiming="{
// Used for the digit spin animations.
// Will fall back to `transformTiming` if unset:
duration: 750, easing: 'linear(...)'
}"
:opacityTiming="{
// Used for fading in/out characters:
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.
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 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 unwanted repositioning 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.
plugins: Plugin[]
Plugins to apply to the component. Currently there’s only one plugin, continuous
, which
makes the number transitions appear to pass through in-between numbers:
<script setup>
import NumberFlow, { continuous } from '@number-flow/vue'
</script>
<template>
<NumberFlow
:plugins="[continuous]"
:value="123"
/>
</template>
This plugin has no effect if trend
is 0
.
Emits
animationsstart: (e: CustomEvent) => void
Triggered when update animations start.
Not to be confused with the built-in
animationstart
event<NumberFlow>
element itself.
animationsfinish: (e: CustomEvent) => void
Triggered when update animations finish.
Styling
NumberFlow uses a
number-flow-vue::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-vue::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 class="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 a <NumberFlow>
affects another <NumberFlow>
’s position, you can wrap
them in a <NumberFlowGroup>
to properly sync their transitions:
<script setup lang="ts">
import NumberFlow, { NumberFlowGroup } from '@number-flow/vue'
const { value, diff } = defineProps<{
value: number
diff: number
}>()
</script>
<template>
<NumberFlowGroup>
<div style="--number-flow-char-height: 0.85em" class="flex items-center gap-4 font-semibold">
<NumberFlow :value :format="{ style: 'currency', currency: 'USD' }" class="~text-2xl/4xl" />
<NumberFlow
:value="diff"
:format="{ style: 'percent', maximumFractionDigits: 2, signDisplay: 'always' }"
:class="[
'~text-lg/2xl transition-colors duration-300',
diff < 0 ? 'text-red-500' : 'text-emerald-500'
]"
/>
</div>
</NumberFlowGroup>
</template>
<NumberFlowGroup>
doesn’t render an element or accept any props.
Composables
useCanAnimate(opts?: { respectMotionPreference?: MaybeRefOrGetter<boolean> }): ComputedRef<boolean>
Returns a computed ref whose value is true
if NumberFlow can animate, i.e. the browser supports the
Limitations
andScientific notations aren’t supported.engineering andNon-Latin digits aren’t currently supported.RTL locales - Backgrounds and borders on
<NumberFlow>
won’t scale smoothly during transitions.