react-countdown, a configurable countdown component with a matching hook
A customizable countdown component for React.
At a glance
- What is it?
- react-countdown is a TypeScript package that provides a countdown component and a useCountdown hook for React, with a custom renderer, a control api and an overtime mode that turns the timer into a stopwatch. The stable line is v2.3.6, while v3.0.0-beta.0 is published under a separate tag, which is the first decision an adopter has to make.
- Who is it for?
- react-countdown suits React teams that need a deadline countdown or a stopwatch with a custom look and a small dependency surface, and it is a poor fit for anything needing recurring schedules, timezone aware targets or a component whose internals you would rather control yourself.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One package, two ways to render a deadline
Most countdown needs in a React application are the same: show the time remaining until a date, change it when the user wants something else, and know when it has run out. react-countdown packages that as a component and, separately, as a hook, so the choice is about how much of the rendering you want to own.
The component takes a renderer callback and calls it with the remaining time, which means the markup is entirely yours. The hook returns the same values and lets you place them in your own JSX. The README documents both under separate headings, and the package description calls it a customizable countdown component and Hook for React.
What it is not is a scheduling library. There is no recurrence, no timezone handling, no locale aware formatting of a target date. The unit of work is a single date and the delta to it. If your requirement is a daily digest timer or a business hours deadline, that is a different problem and this package does not attempt it.
The project itself frames the origin plainly: the countdown started inside a small web app and was extracted into its own package as a way to explore building with React, testing with Jest and publishing an open source project. That history explains the shape of the API, which is small and legible rather than configurable for every case.
How the ticking works: date, total and formatted values
The core input is the date prop, and it accepts a Date, a string or a number timestamp, with the number form treated as a timestamp. The component computes the difference between that target and the current time, and that difference is what everything else is derived from.
Two views of the delta come out of it. The raw values are hours, minutes and seconds, and the formatted values are the same numbers presented as strings, with the negative sign handled by the default renderer once the total turns negative. The rendered props also carry total, the whole delta, and completed, a flag that says the countdown has finished.
The tick rate is adjustable through intervalDelay, and the README is explicit about the relationship between the two settings for sub-second display. To show milliseconds you need the intervalDelay value lower than 1000ms and a precision of 1 to 3, plus a renderer callback of your own. A renderer that prints total directly is what the milliseconds example does:
createRoot(document.getElementById('root')).render(
<Countdown
date={Date.now() + 10000}
intervalDelay={0}
precision={3}
renderer={(props) => <div>{props.total}</div>}
/>
);There is also a daysInHours flag for the case where a duration long enough to have days should be expressed in hours rather than days.
The important design point is that the clock is derived from the target date rather than from a counter that is decremented. A tab that is backgrounded and a component that re-renders do not lose time, because the value on screen is always recomputed from the target. That also means the target date, not the start moment, is the only thing you have to keep correct.
Renderer and onComplete: two different ways to react to the end
Custom output and end-of-countdown notification are separate props, and separating them avoids the usual tangle where the rendering function also has to fire a side effect.
The renderer receives the tick values and decides what to draw. The onComplete callback signals that the countdown has finished. In the README's custom rendering example the renderer itself branches on the completed flag and swaps in a different component:
import { createRoot } from 'react-dom/client';
import Countdown from 'react-countdown';
// Random component
const Completionist = () => <span>You are good to go!</span>;
// Renderer callback with completed condition
const renderer = ({ hours, minutes, seconds, completed }) => {
return completed ? (
<Completionist />
) : (
<span>
{hours}:{minutes}:{seconds}
</span>
);and the render call follows separately:
};
createRoot(document.getElementById('root')).render(
<Countdown date={Date.now() + 5000} renderer={renderer} />
);A team that wants to keep the default markup and only fire a callback at the end does not need a renderer at all, only onComplete.
The smallest useful setup is otherwise plain. The basic example renders a countdown from ten seconds with no renderer at all:
import { createRoot } from 'react-dom/client';
import Countdown from 'react-countdown';
createRoot(document.getElementById('root')).render(<Countdown date={Date.now() + 10000} />);The repository ships an examples folder with a Vite setup, and the README says you can clone the repo and run pnpm start inside that folder to explore more of them.
The api object, autoStart and the overtime stopwatch trick
Controlling the timer is done through an api exposed on the render props, with start, pause and stop methods and an isPaused check. The same api is available from the hook.
A stopwatch is not a separate component. The README describes it as a countdown with overtime enabled that starts at the current time: the delta immediately crosses zero and keeps running, so elapsed time counts up. The default renderer would show a minus sign once total turns negative, so the fix is to render the formatted values, which stay positive, instead of the raw total.
import { createRoot } from 'react-dom/client';
import Countdown from 'react-countdown';
createRoot(document.getElementById('root')).render(
<Countdown
date={Date.now()}
overtime
daysInHours
autoStart={false}with the renderer that reads formatted and drives the buttons:
renderer={({ formatted, api }) => (
<div>
<span>
{formatted.hours}:{formatted.minutes}:{formatted.seconds}
</span>
<button onClick={api.start}>{api.isPaused() ? 'Resume' : 'Start'}</button>
<button onClick={api.pause}>Pause</button>
</div>
)}
/>
);Two details in that example are easy to miss. Because the timer starts at zero, the delta is negative and completed is true from the first tick onwards, so a renderer that keys off completed would show the finished state immediately. And autoStart is set to false so the user starts the timer deliberately.
The hook exposes the same values, with api alongside hours, minutes and seconds and an isCompleted check:
const { hours, minutes, seconds, api } = useCountdown(props.current);In the hook example the options are held in a ref, and the README notes that the ref could be omitted by setting freezeProps to true.
Installing and importing: the beta line and the entry points
Installation goes through npm, pnpm or yarn, and the README gives a command for each:
npm install react-countdown --savepnpm add react-countdownyarn add react-countdownThe version choice comes first. The README states that v3 is in beta and should be installed with the @next tag, while the current stable release is documented at the v2.3.6 tag, with a changelog and migration notes in the repository. In practice the beta install command is npm install react-countdown@next, one tag away from the stable command above.\n Imports are designed for tree shaking. The package ships as both ES modules and CommonJS, is fully typed and is side-effect free, so bundlers can drop the parts you do not use. The component and the hook can be imported from the package root:
import Countdown, { useCountdown } from 'react-countdown';or from dedicated entry points, which let a bundler include only one part plus the shared core:
import Countdown from 'react-countdown/component';
import { useCountdown } from 'react-countdown/hook';The package manifest shows what that means at the packaging level, with a separate export condition per entry point:
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
},The repository builds with Rollup, tests with Jest and runs end to end tests through Cypress, with linting through oxlint and oxfmt plus a TypeScript check.
Where a countdown component is the wrong answer
The first limitation is version churn. The published releases show v3.0.0-beta.0 on 2026-06-14, v2.3.6 on 2024-08-10 and v2.3.5 on 2022-11-23. Two different majors, one of them still a beta, with a gap of close to two years between the last v2 release and the v3 beta. A team that installs the current tag and a team that installs the beta are running different APIs, and the README points to a migration section in the changelog for the differences.
The second limitation is scope. A single target date and the delta to it is the whole model. Recurring reminders, working hours, timezones and locale aware date text are outside it. Rendering a deadline in the user's own timezone means formatting that date yourself and passing a timestamp, which is fine, but any scheduling logic on top of that is code you write and maintain.
The third is the dependency question in general. The alternative to adopting the package is to write the timer yourself, and for a simple display that is a small amount of code: hold the target in state, keep an interval, and compute the delta from Date.now() on each tick. What the package adds is the parts that are easy to get subtly wrong, namely completion detection, a custom renderer, a control api and the overtime semantics. If your need is only a static countdown that never resets, writing it yourself avoids a dependency and a version decision. If you need pause, resume and completion callbacks, the package saves more than it costs.
A final caveat is that the README does not document behaviour under server rendering, and the component computes from the current time, so a rendered on the server and a rendered on the client will not necessarily agree on the first paint.
Maintenance state, releases and the MIT licence
The last push to the repository was on 2026-06-14, the same day the v3.0.0-beta.0 release was published, which suggests the current work is concentrated on the v3 line rather than on the stable branch.
The repository has a conventional modern toolchain: pnpm is the declared package manager at version 10.30.1, Rollup produces the dist output, Jest runs the unit tests and Cypress drives the end to end suite. The package publishes only the dist directory, and the build script runs a clean step before rollup, so what consumers receive is generated output rather than the TypeScript sources.
The licence is MIT, which places no obligations on a commercial product beyond keeping the notice, and the project has a CHANGELOG and a CONTRIBUTING file for the upgrade path. That is a licensing observation, not legal advice.
The upgrade cost is concentrated in the version decision rather than in the licence. Moving from v2 to v3 means reading the changelog, because the v3 beta changes the entry points and the way the timer is built, and the two behave differently enough that the README calls out migration notes. Pinning v2.3.6 gives a known API but inherits a line whose last release was on 2024-08-10.
Editorial conclusion
react-countdown suits React teams that need a deadline countdown or a stopwatch with a custom look and a small dependency surface, and it is a poor fit for anything needing recurring schedules, timezone aware targets or a component whose internals you would rather control yourself. Verify first which line you are installing, because npm install react-countdown@next gives you the v3 beta while the stable tag is v2.3.6, then check that the renderer props you need, such as formatted.hours or api.isPaused, exist in the version you pin, and confirm that the intervalDelay and precision settings behave as your millisecond display expects.
Frequently asked questions
How do I install react-countdown?
The README gives npm install react-countdown --save, plus pnpm add react-countdown and yarn add react-countdown. Version 3 is in beta and is installed with npm install react-countdown@next, while the current stable release is documented at the v2.3.6 tag.
How do I show milliseconds with react-countdown?
The README states that the intervalDelay value needs to be lower than 1000ms and that a precision of 1 to 3 should be used, together with a renderer callback. The milliseconds example renders props.total directly.
Can react-countdown work as a stopwatch?
Yes. The README describes a stopwatch as a countdown with overtime enabled that starts at the current time, so the delta immediately crosses zero and keeps running. Because the default renderer prefixes a minus sign once total turns negative, you render the formatted values instead.
What is the difference between the useCountdown hook and the Countdown component?
Both expose the same tick values and the same api with start, pause and stop. The component takes a renderer callback and calls it with the remaining time, while the hook returns the values so you can place them in your own JSX.
Does react-countdown support tree shaking?
The README states that the package ships as both ES modules and CommonJS, is fully typed and is side-effect free, so bundlers can tree-shake it. Dedicated entry points at react-countdown/component and react-countdown/hook let a bundler include only the part you use.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ndresx-react-countdown)Was this useful?