CountUp.js is a dependency-free, lightweight TypeScript/JavaScript library that creates smooth animated counting transitions for numerical values. It provides a highly customizable animation engine that can count up or down between any two numbers with configurable easing, formatting, and visual effects.
npm install countup.jsimport { CountUp, CountUpOptions, CountUpPlugin } from "countup.js";For CommonJS:
const { CountUp } = require("countup.js");For browsers (UMD):
<script src="node_modules/countup.js/dist/countUp.umd.js"></script>
<script>
// CountUp is available as a global
const countUp = new CountUp('targetId', 100);
</script>import { CountUp } from "countup.js";
// Create a CountUp instance
const countUp = new CountUp('targetElementId', 5234);
// Check for errors and start animation
if (!countUp.error) {
countUp.start();
} else {
console.error(countUp.error);
}CountUp.js is built around several key components:
Main animation class for creating smooth numerical counting animations.
/**
* Creates a new CountUp animation instance
* @param target - DOM element ID, element reference, or input element to animate
* @param endVal - Target value to count to, or null to use element's current content
* @param options - Optional configuration for animation behavior
*/
class CountUp {
constructor(
target: string | HTMLElement | HTMLInputElement,
endVal?: number | null,
options?: CountUpOptions
);
/** Library version */
version: string;
/** Target DOM element being animated */
el: HTMLElement | HTMLInputElement;
/** Function used to format numbers during animation */
formattingFn: (num: number) => string;
/** Easing function used during animation */
easingFn?: (t: number, b: number, c: number, d: number) => number;
/** Error message if initialization failed */
error: string;
/** Starting value for the animation */
startVal: number;
/** Animation duration in milliseconds */
duration: number;
/** Whether the animation is currently paused */
paused: boolean;
/** Current frame value during animation */
frameVal: number;
/** Scroll spy one-time execution flag */
once: boolean;
/** Target end value for animation */
endVal?: number | null;
/** Configuration options */
options?: CountUpOptions;
/**
* Start the counting animation
* @param callback - Optional callback to execute when animation completes
* @returns void
*/
start(callback?: (args?: any) => any): void;
/**
* Toggle pause/resume state of the animation
* @returns void
*/
pauseResume(): void;
/**
* Reset animation to starting value
* @returns void
*/
reset(): void;
/**
* Update the end value and restart animation
* @param newEndVal - New target value to count to
* @returns void
*/
update(newEndVal: string | number): void;
/**
* Handle scroll spy functionality - checks if element is in viewport
* @param self - CountUp instance reference for scroll handling
* @returns void
*/
handleScroll(self: CountUp): void;
/**
* Print/display the current value to the target element
* @param val - Numeric value to display
* @returns void
*/
printValue(val: number): void;
/**
* Validate if a value is a valid number
* @param n - Value to validate
* @returns True if value is a valid number
*/
ensureNumber(n: any): boolean;
/**
* Validate and convert a value to number
* @param value - Value to validate and convert
* @returns Converted number or null if invalid
*/
validateValue(value: string | number): number | null;
/**
* Format a number according to the configured options
* @param num - Number to format
* @returns Formatted number string
*/
formatNumber: (num: number) => string;
/**
* Default exponential ease-out easing function
* @param t - Current time
* @param b - Beginning value
* @param c - Change in value
* @param d - Duration
* @returns Eased value
*/
easeOutExpo: (t: number, b: number, c: number, d: number) => number;
/**
* Parse a formatted number string back to numeric value
* @param number - Formatted number string to parse
* @returns Parsed numeric value
*/
parse(number: string): number;
/**
* Animation frame callback function (arrow function property)
* @param timestamp - Animation frame timestamp
* @returns void
*/
count: (timestamp: number) => void;
}Comprehensive configuration interface for customizing animation behavior.
interface CountUpOptions {
/** Starting value for the animation (default: 0) */
startVal?: number;
/** Number of decimal places to display (default: 0) */
decimalPlaces?: number;
/** Animation duration in seconds (default: 2) */
duration?: number;
/** Use thousand separators like 1,000 vs 1000 (default: true) */
useGrouping?: boolean;
/** Use Indian numbering system like 1,00,000 vs 100,000 (default: false) */
useIndianSeparators?: boolean;
/** Enable easing animation (default: true) */
useEasing?: boolean;
/** Threshold for smart easing activation (default: 999) */
smartEasingThreshold?: number;
/** Amount to be eased for numbers above threshold (default: 333) */
smartEasingAmount?: number;
/** Thousands separator character (default: ',') */
separator?: string;
/** Decimal point character (default: '.') */
decimal?: string;
/** Custom easing function for animation (t: current time, b: beginning value, c: change in value, d: duration) */
easingFn?: (t: number, b: number, c: number, d: number) => number;
/** Custom formatting function for displayed results */
formattingFn?: (n: number) => string;
/** Text prepended to the result (default: '') */
prefix?: string;
/** Text appended to the result (default: '') */
suffix?: string;
/** Array of custom numeral characters for substitution */
numerals?: string[];
/** Start animation when target element enters viewport (default: false) */
enableScrollSpy?: boolean;
/** Delay in milliseconds after element enters viewport (default: 200) */
scrollSpyDelay?: number;
/** Run scroll spy animation only once (default: false) */
scrollSpyOnce?: boolean;
/** Callback function executed when animation completes */
onCompleteCallback?: () => any;
/** Callback function executed when animation starts */
onStartCallback?: () => any;
/** Plugin for custom animation rendering */
plugin?: CountUpPlugin;
}Interface for creating custom animation plugins.
interface CountUpPlugin {
/**
* Custom render method for displaying animated values
* @param elem - Target DOM element to render to
* @param formatted - Formatted number string to display
* @returns void
*/
render(elem: HTMLElement, formatted: string): void;
}import { CountUp } from "countup.js";
const countUp = new CountUp('myNumber', 100);
if (!countUp.error) {
countUp.start();
}import { CountUp, CountUpOptions } from "countup.js";
const options: CountUpOptions = {
startVal: 0,
decimalPlaces: 2,
duration: 3,
useEasing: true,
useGrouping: true,
separator: ',',
decimal: '.',
prefix: '$',
suffix: ' USD'
};
const countUp = new CountUp('price', 1234.56, options);
if (!countUp.error) {
countUp.start(() => console.log('Animation complete!'));
}import { CountUp } from "countup.js";
// Animation triggers when element scrolls into view
const countUp = new CountUp('counter', 500, {
enableScrollSpy: true,
scrollSpyDelay: 300,
scrollSpyOnce: true
});
// Manual scroll check if needed
countUp.handleScroll(countUp);import { CountUp } from "countup.js";
const countUp = new CountUp('target', 100);
if (!countUp.error) {
countUp.start();
// Pause/resume animation
setTimeout(() => countUp.pauseResume(), 1000);
setTimeout(() => countUp.pauseResume(), 2000);
// Update to new value
setTimeout(() => countUp.update(200), 3000);
// Reset animation
setTimeout(() => countUp.reset(), 4000);
}import { CountUp } from "countup.js";
const countUp = new CountUp('percentage', 75.5, {
suffix: '%',
decimalPlaces: 1,
formattingFn: (n) => {
// Custom formatting logic
return n.toFixed(1) + '%';
}
});import { CountUp, CountUpPlugin } from "countup.js";
// Custom plugin implementation
const customPlugin: CountUpPlugin = {
render: (elem: HTMLElement, formatted: string) => {
// Custom rendering logic
elem.innerHTML = `<span class="animated-number">${formatted}</span>`;
}
};
const countUp = new CountUp('target', 100, {
plugin: customPlugin
});CountUp.js includes built-in error handling that stores error messages in the error property:
import { CountUp } from "countup.js";
const countUp = new CountUp('nonExistentElement', 100);
if (countUp.error) {
console.error('CountUp initialization failed:', countUp.error);
// Handle error appropriately
} else {
countUp.start();
}Common error scenarios:
CountUp.js works in all modern browsers and includes:
requestAnimationFrame polyfill for older browsers