AI CSS Animations

Guides · · Maxwell Prehoda

11 Number Counter Animation Examples, From CountUp.js to Pure CSS

There are two kinds of number animation, and picking the right one matters more than the library. A count-up runs a value from zero (or the old value) to the target, which suits a stats section that lands once. A digit roll animates only the digits that change, each one spinning like an odometer, which suits live values such as prices, scores and view counts. Both should ease out so the number slows as it arrives, use tabular figures so the width does not jitter, and format the final value for the reader's locale.

Below are 11 examples to copy: the JavaScript libraries most sites use, the component libraries for React, Vue and Svelte, Apple's native version, a CSS-only technique that needs no script at all, and two of ours. Every entry has a live demo from our directory.

1. CountUp.js by inorganik: Best for stats sections that count up once on scroll

Platform: JavaScript, dependency-free, MIT

Projects
Countries
Happy clients
Live from our directory: Stats Counter Animation. Copy the HTML and CSS →

CountUp.js is one of the most used count-up libraries (about 8k GitHub stars). It is a dependency-free class that animates a value by counting to it, with the options a stats section needs: decimal places, grouping (on by default), custom separators and decimal marks, prefix and suffix, Indian-style grouping, and Eastern Arabic or Farsi numerals. The default duration is 2 seconds and the default easing is easeOutExpo.

Two options are worth knowing. `autoAnimate` starts the count when the element becomes visible (with `autoAnimateOnce` so scrolling back does not replay it), and smart easing, above a `smartEasingThreshold` of 999, counts most of a large number quickly and eases only the last 333, so 48,000 does not crawl.

Our Stats Counter Animation below is the CSS version of a stats row: three numbers count up one after another and each suffix pops in once its number lands.

Watch out for: Screen readers can announce every intermediate value if the element is a live region. Keep the final number in the markup (or an aria-label) and let the animation be visual only.

GitHub ↗

2. Odometer by HubSpot: Best for the classic rolling-digit look on simple sites

Platform: JavaScript + CSS, under 3 KB, MIT

downloads this week
Live from our directory: Odometer Number Animation. Copy the HTML and CSS →

Odometer is the classic rolling-digit counter. Add the `odometer` class to an element, change its text, and the digits roll to the new value, with the animation handled entirely in CSS transforms. The library and its largest theme are under 3 KB minified and compressed, it has no dependencies, and a `format` option such as `(,ddd).dd` controls grouping and decimals.

Its themes (including car and digital) show how much the styling carries the effect: the same roll reads as a dashboard, a scoreboard or a clean product stat depending on the digit boxes around it.

Our Odometer Number Animation below does the roll without JavaScript: each column is its own strip, and the right-hand columns spin through extra turns, driven by a `--to` value per column.

Watch out for: The repository's last code push was in 2018, and its stated browser targets start at IE8. It still works, but expect to fix issues yourself rather than wait for releases.

GitHub ↗

3. NumberFlow by Maxwell Barvian: Best for live prices and dashboards in React, Vue or Svelte

Platform: React, Vue, Svelte packages, MIT

Live from our directory: Rolling Price Change Up and Down. Copy the HTML and CSS →

NumberFlow is the modern answer for numbers that change while you watch (about 7.7k GitHub stars). It is a dependency-free, accessible component that transitions automatically whenever its `value` changes: digits spin to their new values while added or removed characters fade. Formatting goes through `Intl.NumberFormat` via its `format` and `locales` props, so currency, percentages and compact notation work without string building.

The `trend` prop decides which way digits spin: by default they follow the change (up for increases, down for decreases), `+1` or `-1` forces a direction, and `0` lets each digit move on its own. `respectMotionPreference` is on by default, so animation turns off for people who prefer reduced motion.

Our Rolling Price Change below shows why direction matters: the digits roll up in green when the price rises and down in red when it falls.

Watch out for: The docs list scientific and engineering notation, non-Latin digits and right-to-left locales as unsupported. Check your markets before building a localised product on it.

GitHub ↗

4. Motion AnimateNumber: Best for React apps already on Motion+

Platform: React + Motion, Motion+ (paid), about 2.5 KB

Live from our directory: Revenue Stat Count Up. Copy the HTML and CSS →

Motion's AnimateNumber adds about 2.5 KB on top of Motion and animates between values using `Intl.NumberFormat` for locale-aware formatting, including currency and compact notation. Its `transition` prop takes Motion's transition options with separate settings for `layout`, `y` and `opacity`, so you can give the digit roll a spring while the fade stays quick.

Springs are the reason to pick it: a number that overshoots very slightly and settles reads as physical, which suits playful products and dashboards built around Motion's other components.

Our Revenue Stat Count Up below is the pure-CSS take on an animated metric: a $1.2M ARR stat rolls up with the tenths spinning past, a +38% chip springs in and a sparkline draws beneath.

Watch out for: AnimateNumber is exclusive to Motion+ members and is not in the free motion package. NumberFlow covers the same ground for free if that is your only need.

5. CSS-Tricks: animating number counters with @property: Best for count-ups with zero JavaScript

Platform: Pure CSS, @property (Baseline 2024)

Live from our directory: Counting Number. Copy the HTML and CSS →

Carter Li's October 2020 CSS-Tricks article introduced the trick that lets CSS count. Register `--num` with `@property` as an `<integer>` so the browser can interpolate it, feed it into `counter-reset`, and print it with `content: counter(num)`. Animate `--num` from 0 to your target with a transition or keyframes, and the pseudo-element shows every whole number on the way.

When the article was written only Chromium supported `@property`; it has since reached Baseline across all major engines, so the technique is now safe for production stats sections and progress labels.

Our Counting Number below is exactly this: a registered integer custom property animated and rendered through a CSS counter, with no JavaScript.

Watch out for: CSS counters only print integers, and the number lives in generated content. Use the article's split-integer trick for decimals, and keep the real value in the DOM or an aria-label for screen readers.

6. SwiftUI numericText transition: Best for native iOS and macOS apps

Platform: SwiftUI, iOS 16+, macOS 13+, watchOS 9+

Live from our directory: Live Scoreboard Score Change. Copy the HTML and CSS →

Apple's own version is one modifier. `contentTransition(.numericText())` tells SwiftUI that a text view shows numbers, and changes to it get a transition tailored to numeric characters that count up or down instead of a crossfade. Pass `countsDown: true` when the value is decreasing so the digits roll the right way. It arrived in iOS 16, macOS 13 and watchOS 9.

It is worth studying even if you only build for the web, because it is the reference for how a live number should feel: only the changed digits move, the motion is short, and direction carries meaning.

Our Live Scoreboard Score Change below is a web take on the same idea: the home score rolls from 102 to 105 with a +3 pop and a sweep of team colour.

Watch out for: Apple's docs say the special transition applies in certain environments, so test it in the context you ship (widgets, Live Activities, regular views) rather than assuming it everywhere.

7. Flip by PQINA: Best for countdowns, fundraising totals and flip clocks

Platform: JavaScript, MIT, free

1 0
Live from our directory: Flip Clock Animation. Copy the HTML and CSS →

Flip, by Rik Schennink of PQINA, is a free, MIT-licensed flip counter plugin with presets for countdowns, counters, clocks, stopwatches and billboards. Beyond the flip itself it solves the problems timers actually have: TimeSnap corrects timer drift and ServerSync keeps the client in step with server time, so a sale countdown does not disagree with the checkout.

Pick a flip counter when the number is the event: a launch countdown, a donation total, a seats-left counter. The split-flap motion says this changes in steps, which suits whole units of time or money.

Our Flip Clock Animation below is a CSS-only flip: the seconds card folds down every second, and the digits come from a registered integer and a cyclic `@counter-style`, so 9 wraps to 0.

Watch out for: A countdown that animates every second is constant motion. Keep it small, avoid animating every unit at once, and give reduced-motion users a static updating number.

8. Remotion number counter: Best for stats in product videos and launch films

Platform: Remotion (React for video)

Series A Funding Announcement. See the prompt →

For video rather than web pages, Remotion's number counter element shows the shape that reads well on screen: it counts from 0 to 24,813 with `Easing.out(Easing.exp)`, a fast start that slows toward the end, over the first 75 percent of the composition, then holds the final value. It rounds each frame and formats with `toLocaleString`, set in Inter at weight 800 with tabular numerals.

The hold is the lesson. In a video the viewer cannot scroll back, so the final number needs time on screen after the count stops.

Our Series A Funding Announcement below, made in our Animation Studio, uses the same idea: the headline builds word by word with the amount rolling into place, and the frame moves on only after the number has landed.

Watch out for: Exponential ease-out spends most of the duration on the last few values. If the count looks stuck near the end, shorten it or switch to a cubic ease-out.

9. Intl.NumberFormat for the final value: Best for counters shown to an international audience

Platform: JavaScript built-in, all modern browsers

Live from our directory: Animated Price Counter. Copy the HTML and CSS →

Every counter on this page ends on a formatted number, and the platform already knows how to format it. `Intl.NumberFormat` formats numbers for a locale: `notation: "compact"` turns 1234567 into 1.2M in English, and `style: "currency"` in de-DE turns 123456.789 into 123.456,79 €. `formatToParts()` returns the pieces (integer, group, decimal, fraction, currency) so you can animate only the digits and leave separators and symbols still.

That last method is how the better counter libraries work under the hood, and it is the fix for counters that jump when a thousands separator appears mid-count.

Our Animated Price Counter below keeps dollars and cents as two registered integers, so the cents can spin and land on .99 while the currency sign stays put.

Watch out for: Format the end value once and reserve its width. Animating a raw number and formatting only at the end makes the layout shift on the last frame.

10. Our Number Animations collection: Best for dashboards, pricing and progress labels in pure CSS

Platform: Motions directory, free, pure CSS Ours

Live from our directory: Percentage Counter Animation. Copy the HTML and CSS →

Every counter in this collection is HTML and CSS with no JavaScript: count-ups on registered integers, odometer strips, flip cards, percentage labels, timers and tickers, each with a `prefers-reduced-motion` guard and custom properties for colour and size. The demo is Percentage Counter Animation: a percentage counts from 0 to 100 while a bar fills on the same easing, then the label flips to Complete.

Use CSS counters when the target is known when the page renders, such as marketing stats, pricing, onboarding progress or a results screen. Reach for a library when values stream in from a server.

Watch out for: CSS count-ups run on page load or when a class is added. For stats below the fold, trigger them with a scroll-driven timeline or a small IntersectionObserver so nobody misses the count.

11. Our Slot Machine Number Animation: Best for rewards, giveaways and game results

Platform: Motions directory, free, pure CSS Ours

Jackpot
Live from our directory: Slot Machine Number Animation. Copy the HTML and CSS →

Three reels spin with motion blur and stop one after another, each overshooting a touch before it settles on 7, then JACKPOT fades in. Each reel is a digit strip with its own duration, so the staggered stop comes from timing alone.

A slot reveal is a number animation that builds suspense on purpose, which makes it right for reward moments (a discount code, a points total, a raffle draw) and wrong for any number someone is waiting to read. For more celebration moments see our success animation examples.

Watch out for: Suspense is only fun when the outcome is good news. Never use a slot reveal for prices, balances or anything the user might find disappointing.

How to choose a number counter animation

Match it to how the number behaves

A number that lands once (a stats row, a results screen) wants a count-up with ease-out: CountUp.js, or our pure-CSS Counting Number. A number that keeps changing (a price, a score, a follower count) wants a digit roll that moves only what changed, with direction: NumberFlow, AnimateNumber or SwiftUI's numericText. A number in whole steps of time or money wants a flip, like PQINA's Flip or our Flip Clock.

Match it to your stack

No JavaScript: @property integers with CSS counters, from our Number Animations collection. Vanilla JavaScript: CountUp.js, or Odometer for the classic look if you accept an unmaintained dependency. React, Vue or Svelte: NumberFlow. Motion+ projects: AnimateNumber. Native Apple apps: contentTransition(.numericText()). Video: Remotion's counter pattern or our Studio.

Match it to the reader

Use tabular figures (font-variant-numeric: tabular-nums) so digits do not jitter, format with Intl.NumberFormat for the reader's locale, keep count-ups between about 1 and 2 seconds, and make sure the final value is in the DOM for screen readers and search engines. Under prefers-reduced-motion, show the final number immediately; NumberFlow does this by default.

Make your stats count this afternoon

Take the stats row on your homepage. Set the numbers in tabular figures, put the final formatted values in the HTML, and animate them from zero over 1.5 seconds with an ease-out when the row scrolls into view, once. Add the suffix (+, %, k) after each number lands. That is the version that looks good, reads correctly and still works with JavaScript off.

Copy a ready counter from the Number Animations collection, or describe the number you need and get production-ready CSS from the creator. Counters pair naturally with bars and rings; see our progress bar animation examples.

A prompt to start from
Make a pure CSS stats row with three counters: 12,400+ customers, 99.9% uptime and 38 countries.
Each number counts up from 0 using a registered @property integer and a CSS counter, 1.5 seconds with an ease-out, staggered 200 ms.
Use tabular-nums, keep the final values in aria-labels, and pop each suffix in after its number lands.
Start when the row scrolls into view with a view() timeline where supported.
Under prefers-reduced-motion, show the final numbers immediately.

Make your own with AI →

Common questions

How do you make a number counter animation in CSS?

Register a custom property with @property as an integer, set counter-reset: num var(--num) on an element, and show it with content: counter(num) on a pseudo-element. Then animate --num from 0 to the target with a transition or @keyframes. The browser interpolates the integer and the counter prints each value. Carter Li described the technique on CSS-Tricks in 2020, and @property is now Baseline.

What is the best JavaScript library for a count-up animation?

CountUp.js is one of the most widely used: dependency-free, MIT licensed, with grouping, prefixes and suffixes, a 2 second default duration with easeOutExpo, and an autoAnimate option that starts the count when the element appears. For numbers that change repeatedly in React, Vue or Svelte, NumberFlow is a better fit.

How do I start a counter when it scrolls into view?

With CountUp.js, set autoAnimate to true (and autoAnimateOnce to avoid replays). In plain JavaScript, use an IntersectionObserver to add a class that starts a CSS animation. In CSS alone, attach the animation to a view() scroll timeline where supported.

Why does my animated number jitter or shift the layout?

Proportional digits have different widths, so the number changes width as it counts. Use font-variant-numeric: tabular-nums and reserve the width of the final formatted value, including separators and currency symbols, before the animation starts.

Are animated counters accessible?

They can be if the final value is always available. Put the real number in the markup or an aria-label, avoid aria-live on a counting element so screen readers do not announce every step, and show the final value immediately under prefers-reduced-motion.

Sources