
It works well for stats sections, dashboard metrics, pricing counters, progress numbers, and scroll-triggered KPI blocks.
The package supports ESM imports, a UMD browser build, TypeScript declarations, custom formatting, callbacks, and animation plugins.
Features
- Animates numbers from a start value to an end value.
- Counts up and down based on the target value.
- Formats decimals, separators, prefixes, and suffixes.
- Substitutes custom numeral glyphs.
- Applies smart easing to large values.
- Starts counters when elements appear on screen.
- Supports ESM imports and UMD browser scripts.
- Includes TypeScript declarations.
- Accepts animation plugins for custom rendering.
- Cleans up observers and callbacks after teardown.
Use Cases
- Add animated revenue, user, or download counters to landing pages.
- Display KPI cards in admin dashboards.
- Trigger stats animations inside long scrolling pages.
- Animate pricing, savings, or progress numbers.
- Format localized numeric counters with custom separators.
How To Use CountUp.js
Install with npm
npm install countup.js
Import CountUp In A Build System
import { CountUp } from 'countup.js';
// Create a counter for the element with id="monthly-revenue".
const revenueCounter = new CountUp('monthly-revenue', 48500, {
prefix: '$',
separator: ',',
duration: 2.4
});
// Start the animation after CountUp validates the target element.
if (!revenueCounter.error) {
revenueCounter.start();
} else {
console.error(revenueCounter.error);
}
Use The UMD Browser Build
<div id="active-users">0</div>
<script src="dist/countUp.umd.js"></script>
<script>
// The UMD build exposes CountUp on the countUp namespace.
const usersCounter = new countUp.CountUp('active-users', 12750, {
separator: ',',
suffix: ' users'
});
// Start the counter after the instance passes validation.
if (!usersCounter.error) {
usersCounter.start();
}
</script>
Use A Local ESM File
<div id="orders-counter">0</div> <script src="./main.js" type="module"></script>
import { CountUp } from './js/countUp.min.js';
window.addEventListener('load', function() {
// Count from 0 to the final order count.
const ordersCounter = new CountUp('orders-counter', 3200, {
separator: ','
});
// Start after the page finishes loading.
ordersCounter.start();
});
Module scripts need a local server in many browsers. A direct file path can trigger a CORS error when the browser loads type="module" scripts from disk.
Count Down From A Custom Start Value
<div id="remaining-seats">120</div>
import { CountUp } from 'countup.js';
// Count down from 120 to 35.
const seatsCounter = new CountUp('remaining-seats', 35, {
startVal: 120,
duration: 1.8
});
seatsCounter.start();
Format Currency, Decimals, And Suffixes
<div id="conversion-rate">0</div>
import { CountUp } from 'countup.js';
// Show one decimal place and append a percent sign.
const rateCounter = new CountUp('conversion-rate', 18.7, {
decimalPlaces: 1,
decimal: '.',
suffix: '%',
duration: 2
});
rateCounter.start();
Start The Counter When It Appears On Screen
<section class="stats-panel"> <span id="completed-projects">0</span> </section>
import { CountUp } from 'countup.js';
// autoAnimate starts the counter when the target becomes visible.
const projectCounter = new CountUp('completed-projects', 860, {
autoAnimate: true,
autoAnimateDelay: 150,
autoAnimateOnce: true
});
// Do not call start() when autoAnimate controls the animation.
if (projectCounter.error) {
console.error(projectCounter.error);
}
autoAnimate uses IntersectionObserver in current releases. Older CountUp.js versions used enableScrollSpy, scrollSpyDelay, and scrollSpyOnce. Those names now exist as deprecated options.
Keep Digits Stable During Animation
.stats-panel span {
font-variant-numeric: tabular-nums;
}
font-variant-numeric: tabular-nums helps counters look steadier in fonts that use variable-width digits.
Configuration Options
startVal(number): Sets the number where the animation begins. Default:0.decimalPlaces(number): Sets the number of digits after the decimal point. Default:0.duration(number): Sets the animation length in seconds. Default:2.useGrouping(boolean): Adds grouping separators to large numbers. Default:true.useIndianSeparators(boolean): Uses Indian-style number grouping. Default:false.useEasing(boolean): Applies easing to the counter animation. Default:true.smartEasingThreshold(number): Sets the value threshold where smart easing begins. Default:999.smartEasingAmount(number): Sets the eased amount for values above the smart easing threshold. Default:333.separator(string): Sets the grouping separator character. Default:','.decimal(string): Sets the decimal character. Default:'.'.easingFn(function): Sets a custom easing function.formattingFn(function): Sets a custom formatter for the rendered value.prefix(string): Adds text before the number. Default:''.suffix(string): Adds text after the number. Default:''.numerals(string[]): Replaces the default digits with custom numeral glyphs.onCompleteCallback(function): Runs after the animation completes.onStartCallback(function): Runs when the animation starts.plugin(CountUpPlugin): Uses a custom renderer for alternate animation styles.autoAnimate(boolean): Starts the animation when the target becomes visible. Default:false.autoAnimateDelay(number): Sets the delay in milliseconds after auto animation triggers. Default:200.autoAnimateOnce(boolean): Runs auto animation only once. Default:false.enableScrollSpy(boolean): Deprecated. UseautoAnimate.scrollSpyDelay(number): Deprecated. UseautoAnimateDelay.scrollSpyOnce(boolean): Deprecated. UseautoAnimateOnce.
API Methods
// Starts the counter animation.
salesCounter.start();
// Starts the animation and runs a callback after completion.
salesCounter.start(function() {
console.log('Revenue counter finished.');
});
// Pauses the active animation or resumes a paused animation.
salesCounter.pauseResume();
// Resets the counter to its starting value.
salesCounter.reset();
// Updates the end value and animates to the new number.
salesCounter.update(72500);
// Cancels animation work and clears observers and callbacks.
salesCounter.onDestroy();
Events
CountUp.js does not provide a named event system. Use onStartCallback, onCompleteCallback, or the callback argument passed to start() when you need lifecycle hooks.
import { CountUp } from 'countup.js';
const signupCounter = new CountUp('signup-count', 2400, {
// Runs when the animation starts.
onStartCallback: function() {
console.log('Signup counter started.');
},
// Runs after the animation completes.
onCompleteCallback: function() {
console.log('Signup counter completed.');
}
});
signupCounter.start();
Plugin Example
CountUp.js supports custom animation plugins. A plugin provides a render() method that receives the target element and the formatted value for each frame.
import { CountUp } from 'countup.js';
import { Odometer } from 'odometer_countup';
// Use the Odometer plugin for a rolling digit effect.
const inventoryCounter = new CountUp('inventory-total', 99999, {
plugin: new Odometer({
duration: 2.3,
lastDigitDelay: 0
}),
duration: 3
});
inventoryCounter.start();
Alternatives
FAQs
Q: What is CountUp.js used for?
A: CountUp.js animates numeric values for stats blocks, dashboards, pricing counters, and scroll-triggered KPI sections.
Q: Can CountUp.js count down?
A: Yes. Set a higher startVal and a lower end value.
Q: Does CountUp.js support TypeScript?
A: Yes. The npm package includes TypeScript declaration files.
Q: How do I start CountUp.js when the element appears on screen?
A: Set autoAnimate: true and create the instance. Do not call start() for that counter.
Q: Why does my module import fail from a local HTML file?
A: Browser module scripts often need a local server. Run the page through a local development server.
Q: What replaced enableScrollSpy?
A: Use autoAnimate. Use autoAnimateDelay and autoAnimateOnce for the matching delay and once-only behavior.
Changelog
05/08/2026
- Rewrite the doc
v2.10.0 (06/02/2025)
- This release modernizes the “scroll spy” mechanism to use an intersection observer instead of a window on-scroll handler. This also supports triggering animation when the target element becomes visible for other reasons like a modal opening or a parent div becoming visible, so several options have been renamed and old ones deprecated.
v2.9.0 (06/02/2025)
- Allows getting the endVal from the target element
v2.8.1 (04/23/2025)
- Provide package.json “exports” for compatibility with modern build systems.
v2.8.0 (08/26/2023)
- Added onStartCallback option, useful for when scroll spy is enabled
v2.7.0 (06/29/2023)
- update
v2.6.2 (05/01/2023)
- bugfix
v2.6.1 (05/01/2023)
- rebuild
v2.6.0 (03/13/2023)
- Support animation plugins: A plugin is a class instance or object passed in options for the plugin param that implements a render method.
- The plugin’s render method will be called on each frame to display the formatted value instead of CountUp’s.
v2.5.0 (03/01/2023)
- Added new option, onCompleteCallback which gets called when the animation completes. You can still pass a callback to the start method, and it will override whatever is passed in the options.
v2.4.2 (01/29/2023)
- Make it so when scroll spy is enabled, and scrollSpyOnce is false, CountUp will re-animate when scrolling up, as well as down
v2.4.1 (01/25/2023)
- Added Indian separators option useIndianSeparators which will format a number like “1,00,000” instead of “100,000”
- Added null check in printValue method to potentially fix react issue
v2.3.2 (07/10/2022)
- Fixed a bug where, when using smart easing and counting down, the animation would take longer than configured. It also fixed an issue which caused 2 easing cycles to run for the same scenario.
v2.3.1 (06/29/2022)
- Fix window check for SSR
v2.3.0 (06/28/2022)
- Fixes an issue where, when the counter element’s parent is position: relative the scrollSpy function does not trigger when the element scrolls into the view
v2.2.0 (05/18/2022)
- Added an option to make scroll spy triggered animations run only once
v2.1.0 (03/03/2022)
- New scroll spy option to trigger animation when the element is scrolled into view.
v2.0.8 (07/28/2020)
- Add “module” in package.json
- remove TSLint
- add ESLint
v2.0.7 (08/26/2020)
- fixed: Value is not updated correctly when navigating quickly to value
v2.0.6 (08/08/2020)
- allow certain options to be changed after instantiation
- UMD module now included
v2.0.4 (06/20/2019)
- Rewritten in Typescript
v1.9.3 (09/21/2018)
- Update
The post CountUp.js Guide: Animated Number Counter Options & Examples appeared first on CSS Script.
Discover more from RSS Feeds Cloud
Subscribe to get the latest posts sent to your email.
