React CountUp
react-countup is a React component that provides an easy-to-use wrapper around the CountUp.js library, enabling numerical value animations within React applications. As of version 6.5.3, the library is actively maintained, with frequent patch and minor releases primarily focused on dependency updates, bug fixes, and incorporating new features from the underlying countup.js library, such as `useIndianSeparators` and scroll spy enhancements. It offers both a declarative component API (<CountUp />), a flexible render prop pattern, and a `useCountUp` hook for more fine-grained imperative control over the animation instance. Key differentiators include its robust API for controlling animation duration, delay, decimal and grouping separators, prefixes/suffixes, easing functions, and scroll-triggered animations, abstracting away the direct CountUp.js instance management. It also ships with TypeScript types, enhancing developer experience in typed projects.
Common errors
-
Invariant Violation: Invalid hook call. Hooks can only be called inside of the body of a function component.
cause Attempting to use the `useCountUp` hook outside of a React function component or a custom hook.fixEnsure `useCountUp` is called directly within the body of a React function component or another custom hook, strictly adhering to React's Rules of Hooks. -
TypeError: Cannot read properties of undefined (reading 'current') / Cannot access 'countUpRef' before initialization
cause This error typically occurs when attempting to access the `countUpRef` or other imperative methods from the render prop API before the component has fully mounted or when the ref is not yet assigned.fixInteract with the CountUp instance methods (e.g., `start`, `pauseResume`) only when the component is mounted and the ref is available, often within React's `useEffect` hook or event handlers that trigger after initial render. -
CountUp animation does not restart when the numeric 'end' prop changes.
cause By default, the `CountUp` component does not re-animate if the `end` prop changes after its initial render; it simply updates the displayed value without animation.fixTo make the animation restart when the `end` prop changes, you must explicitly add the `redraw` prop to the `CountUp` component: `<CountUp end={newValue} redraw />`.
Warnings
- breaking Major version updates (v3, v4, v5, v6) typically introduce breaking changes in API, prop names, or component behavior. Always consult the specific version's release notes and migration guides on GitHub when upgrading across major versions.
- gotcha The `redraw` prop is essential for re-triggering the animation when the `end` value changes. If `redraw` is not set to `true`, the CountUp component will only display the new `end` value without animating from the previous value.
- gotcha The `v6.5.3` release note 'Exclude esm build' is ambiguous. While likely a fix for bundling, it could subtly alter ESM module resolution or availability, potentially affecting specific build toolchains or module loaders. If you encounter issues with ESM imports after updating, investigate your build configuration.
- gotcha When using react-countup in Server-Side Rendering (SSR) environments, you might encounter issues due to its reliance on browser-specific APIs for animation. While v6.3.2 included fixes for SSR conditions, careful implementation is still required.
Install
-
npm install react-countup -
yarn add react-countup -
pnpm add react-countup
Imports
- CountUp
import CountUp from 'react-countup'
import { CountUp } from 'react-countup' - useCountUp
const useCountUp = require('react-countup').useCountUpimport { useCountUp } from 'react-countup' - CountUpProps
import type { CountUpProps } from 'react-countup'
Quickstart
import React, { useState } from 'react';
import { CountUp, useCountUp } from 'react-countup';
const App: React.FC = () => {
const [dynamicValue, setDynamicValue] = useState(500);
// Example using the useCountUp hook for imperative control
const { countUp, start, pauseResume, reset, update } = useCountUp({
start: 0,
end: 100,
delay: 1000,
duration: 5,
onStart: () => console.log('Hook animation started!'),
onEnd: () => console.log('Hook animation ended!'),
});
return (
<div>
<h1>React CountUp Examples</h1>
<h2>Simple Component:</h2>
<CountUp
start={0}
end={2024}
duration={3}
delay={0.5}
prefix="Year: "
suffix="!"
separator=","
decimals={0}
useEasing={true}
onEnd={() => console.log('Component animation finished!')}
/>
<br /><br />
<h2>Render Prop Example:</h2>
<CountUp start={500} end={1000} duration={2} delay={1} useGrouping={false}>
{({ countUpRef, start: renderPropStart }) => (
<div>
<span ref={countUpRef} />
<button onClick={renderPropStart}>Start Render Prop</button>
</div>
)}
</CountUp>
<br /><br />
<h2>Hook (Imperative Control):</h2>
<div>
<p>Current Hook Value: {countUp}</p>
<button onClick={start}>Start Hook</button>
<button onClick={pauseResume}>Pause/Resume Hook</button>
<button onClick={reset}>Reset Hook</button>
<button onClick={() => update(Math.random() * 500 + 100)}>Update Hook</button>
</div>
<br /><br />
<h2>Dynamic Value with 'redraw' prop:</h2>
<button onClick={() => setDynamicValue(Math.floor(Math.random() * 1000))}>
Change Value to: {dynamicValue}
</button>
<br />
<CountUp
start={0}
end={dynamicValue}
duration={2}
redraw={true}
className="dynamic-counter"
/>
</div>
);
};
export default App;