Basic usage
<!-- Basic usage -->
<script>
import NumberFlow from '@number-flow/svelte'
</script>
<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} />
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}
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(0,0.008,0.028,0.06,0.099,0.145,0.196,0.249,0.304,0.36,0.416,0.47,0.523,0.573,0.621,0.667,0.709,0.748,0.784,0.817,0.847,0.874,0.898,0.92,0.939,0.955,0.97,0.982,0.992,1.001,1.008,1.014,1.019,1.022,1.025,1.027,1.028,1.028,1.028,1.028,1.027,1.026,1.025,1.024,1.022,1.02,1.019,1.017,1.016,1.014,1.013,1.011,1.01,1.009,1.008,1.007,1.006,1.005,1.004,1.003,1.003,1.002,1.001,1.001,1.001,1,1,1,1,1,0.999,0.999,0.999,0.999,1)'
}}
spinTiming={{
// Used for the digit spin animations.
// Will fall back to `transformTiming` if unset:
duration: 750, easing: 'linear(0,0.008,0.028,0.06,0.099,0.145,0.196,0.249,0.304,0.36,0.416,0.47,0.523,0.573,0.621,0.667,0.709,0.748,0.784,0.817,0.847,0.874,0.898,0.92,0.939,0.955,0.97,0.982,0.992,1.001,1.008,1.014,1.019,1.022,1.025,1.027,1.028,1.028,1.028,1.028,1.027,1.026,1.025,1.024,1.022,1.02,1.019,1.017,1.016,1.014,1.013,1.011,1.01,1.009,1.008,1.007,1.006,1.005,1.004,1.003,1.003,1.002,1.001,1.001,1.001,1,1,1,1,1,0.999,0.999,0.999,0.999,1)'
}}
opacityTiming={{
// Used for fading in/out characters:
duration: 350, easing: 'ease-out'
}}
/>
For spring-based easings, I’d recommend
trend: boolean'increasing''decreasing'
Controls the direction of the digits.
true:
The digits go up if the number increases, and down if the number decreases.false:
Ignores the trend of the number; 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 increase or decrease ( ).example "increasing":
The digits always go up."decreasing":
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 false
.
animated: boolean
Can be set to false
to disable all animations and finish any current ones.
See the
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.
Events
animationsstart: (e: CustomEvent) => void
Triggered when update animations start.
animationsfinish: (e: CustomEvent) => void
Triggered when update animations finish.
Styling
NumberFlow uses a
number-flow-svelte::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-svelte::part(suffix) {
}
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-muted" />
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:
<script lang="ts">
import NumberFlow, { NumberFlowGroup } from '@number-flow/svelte'
import clsx from 'clsx/lite'
const {
value,
diff
}: {
value: number
diff: number
} = $props()
</script>
<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={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.
Stores
getCanAnimate(opts?: { respectMotionPreference?: boolean }): Readable<boolean>
Returns a readable store whose value is true
if NumerFlow 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.