React Odometer.js
React Odometer.js is a wrapper component that integrates the Odometer.js library into React applications, providing animated numerical counters. The current stable version is 3.1.3. While it doesn't follow a strict release cadence, updates appear to coincide with significant React API changes or new features, such as the adoption of hooks in v3.0.0 for React 16.8+. Its primary differentiator is simplifying the use of Odometer.js within a React ecosystem, abstracting away direct DOM manipulation typically required by the underlying library. It ships with TypeScript types, enhancing developer experience and type safety. A key aspect of its usage is managing server-side rendering (SSR) environments like Next.js or Gatsby, where dynamic imports are necessary to prevent issues with Odometer.js's reliance on the browser's `document` object.
Common errors
-
ReferenceError: document is not defined
cause Attempting to render `react-odometerjs` on the server-side in an SSR environment (e.g., Next.js, Gatsby) without dynamically importing it.fixUse a dynamic import for the `Odometer` component with `ssr: false`. Example for Next.js: `const Odometer = dynamic(import('react-odometerjs'), { ssr: false, loading: () => 0 });` -
Objects are not valid as a React child (found: object with keys {format}). If you meant to render a collection of children, use an array instead.cause Passing Odometer options as an object to an `options` prop, which was deprecated and removed in v2.0.0.fixPass Odometer options directly as props to the component. Change `<Odometer value={1234} options={{ format: '(.ddd),dd' }} />` to `<Odometer value={1234} format="(.ddd),dd" />`. -
TypeError: Cannot read properties of null (reading 'style') OR Odometer is not defined
cause The Odometer.js library or its React wrapper attempts to access DOM elements before the required CSS theme is loaded or the component is properly mounted, or due to incorrect theme application.fixEnsure that the `odometer` npm package is installed (`npm install odometer`) and a theme CSS file (e.g., `odometer/themes/odometer-theme-default.css`) is properly imported and applied globally or in the component's scope. Also, confirm the `theme` prop, if used, matches the imported CSS. -
Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
cause `react-odometerjs` v3.0.0+ utilizes React Hooks internally, but your project's `react` and `react-dom` peer dependencies are older than the required 16.8.0 version.fixUpdate your `react` and `react-dom` packages in your project to version `16.8.0` or higher to support React Hooks.
Warnings
- breaking Minimum React version increased to 16.8.0 due to the adoption of React Hooks, which are used internally by `react-odometerjs`.
- breaking Odometer options are now passed directly as props to the component, rather than nested within an `options` object.
- deprecated The `selector` prop was removed as it had no effect when `auto: false`, which is the default configuration for this React wrapper.
- gotcha When using `react-odometerjs` with Server-Side Rendering (SSR) frameworks like Next.js or Gatsby, the underlying `Odometer.js` library directly accesses the browser's `document` object, leading to 'document is not defined' errors during server builds.
- gotcha To display correctly, `react-odometerjs` requires an Odometer.js CSS theme file to be manually imported into your application's `head` or component stylesheet.
- gotcha Runtime Prop Types are no longer bundled with the npm package since v2.1.0, relying instead on TypeScript types for type checking.
Install
-
npm install react-odometerjs -
yarn add react-odometerjs -
pnpm add react-odometerjs
Imports
- Odometer
import { Odometer } from 'react-odometerjs'; import * as Odometer from 'react-odometerjs';import Odometer from 'react-odometerjs';
- Odometer (CommonJS)
const Odometer = require('react-odometerjs');(Use ESM import)
- OdometerProps
import { OdometerProps } from 'react-odometerjs';import type { OdometerProps } from 'react-odometerjs';
Quickstart
import React, { useEffect, useState } from 'react';
import Odometer from 'react-odometerjs';
// To use themes, you must install the original 'odometer' package:
// npm install odometer
// Then, import the desired theme's CSS file. Example for default theme:
import 'odometer/themes/odometer-theme-default.css';
const OdometerExample = () => {
const [value, setValue] = useState(1234);
const [isMounted, setIsMounted] = useState(false); // State to ensure Odometer is mounted on client-side
useEffect(() => {
// This ensures the Odometer component is only rendered client-side,
// which is crucial for Odometer.js as it relies on the 'document' object.
// For SSR frameworks, more robust dynamic imports are needed (see warnings).
setIsMounted(true);
const timeoutId = setTimeout(() => setValue(4321), 2000);
return () => {
clearTimeout(timeoutId);
};
}, []);
// Render a placeholder or nothing if not mounted to prevent SSR issues
if (!isMounted) {
return <div>Loading counter...</div>;
}
return (
<div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
<h1>Animated Counter Example</h1>
<p>Watch the number change after 2 seconds:</p>
<Odometer
value={value}
format="(.ddd),dd" // Example format: thousands separator and two decimal places (not shown here as value is integer)
theme="default" // Corresponds to 'odometer-theme-default.css'
animation="count" // Simple counting animation
duration={1500} // Animation duration in milliseconds
style={{ fontSize: '4em', fontWeight: 'bold', color: '#007bff' }}
/>
<p style={{ marginTop: '20px' }}>Current value: {value}</p>
</div>
);
};
export default OdometerExample;