Written by Jude Miracle✏️
Countdown timers are essential features in user interfaces. They are used to create urgency and encourage user engagement in various experiences. Countdown timers are especially useful in coordinating virtual events across different time zones, organizing event registrations, scheduling ecommerce sales, and much more.
In this article, we will look at some of the best React countdown component libraries and explore how to create your own custom timers.
Building your own timer using the React useRef
Hook
Before using existing libraries for countdown timers, it’s useful to know how to create one from scratch with React useRef
Hook. Understanding this will help you see the challenges that countdown libraries address and give you the skills to make your own library when necessary.
Understanding countdown concepts
A countdown timer has three main parts: time storage, time updates, and cleanup. The useRef
Hook is important because it helps us keep track of the timer interval without causing the component to re-render each time.
Let's break down the implementation step by step:
import React, { useState, useRef, useEffect } from 'react';
const CountdownTimer = ({ initialTime, onComplete }) => {
// State to hold the current time remaining
const [timeRemaining, setTimeRemaining] = useState(initialTime);
// Reference to hold our interval ID
const intervalRef = useRef(null);
useEffect(() => {
// Start the countdown
intervalRef.current = setInterval(() => {
setTimeRemaining(prevTime => {
if (prevTime <= 1) {
// Clear interval when we reach zero
clearInterval(intervalRef.current);
onComplete?.(); // Call completion handler if provided
return 0;
}
return prevTime - 1;
});
}, 1000);
// Cleanup function to clear interval when component unmounts
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
}, []); // Empty dependency array means this effect runs once on mount
// Convert seconds to hours, minutes, seconds
const hours = Math.floor(timeRemaining / 3600);
const minutes = Math.floor((timeRemaining % 3600) / 60);
const seconds = timeRemaining % 60;
return (
<div className="countdown-timer">
{hours.toString().padStart(2, '0')}:
{minutes.toString().padStart(2, '0')}:
{seconds.toString().padStart(2, '0')}
</div>
);
};
First, we use useState
to maintain the current countdown value. While we could store this in a ref
as well, using state ensures our component re-renders when the time changes, updating what the user sees. Learn the difference between useState
and useRef
in this guide.
The useRef
Hook creates our intervalRef
, which stores the ID returned by setInterval
. This reference is important because it persists between renders without causing re-renders itself, allows us to clear the interval from any point in our component, and prevents memory leaks by enabling cleanup when the component unmounts.
The useEffect
Hook orchestrates our timer's lifecycle. Inside it, we set up the interval to decrement our timer every second, use a cleanup function to clear the interval when the component unmounts, and implement the empty dependency array to ensure our effect runs only once when the component mounts.
While this simple version works, a countdown timer for real use can consider several special cases:
import React, { useState, useRef, useEffect } from 'react';
const EnhancedCountdownTimer = ({
initialTime,
onComplete,
onTick,
isPaused = false
}) => {
const [timeRemaining, setTimeRemaining] = useState(initialTime);
const intervalRef = useRef(null);
const previousTime = useRef(Date.now());
const clearTimer = () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
};
useEffect(() => {
if (isPaused) {
clearTimer();
return;
}
intervalRef.current = setInterval(() => {
const currentTime = Date.now();
const deltaTime = Math.floor((currentTime - previousTime.current) / 1000);
previousTime.current = currentTime;
setTimeRemaining(prevTime => {
const newTime = Math.max(0, prevTime - deltaTime);
onTick?.(newTime); // Notify parent of time update
if (newTime === 0) {
clearTimer();
onComplete?.();
}
return newTime;
});
}, 1000);
return clearTimer;
}, [isPaused, onComplete, onTick]);
// Reset hook to handle initialTime changes
useEffect(() => {
setTimeRemaining(initialTime);
previousTime.current = Date.now();
}, [initialTime]);
return (
<div className="countdown-timer">
{formatTime(timeRemaining)}
</div>
);
};
// Helper function to format time
const formatTime = (totalSeconds) => {
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = totalSeconds % 60;
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
};
This improved version considers several real use case factors:
- We track the actual time between ticks so that we can adjust for when
setTimeout
orsetInterval
don’t run exactly on time due to browser issues or system load - Users can pause and resume the timer using the
isPaused
prop, showing how to manage user actions - The
onTick
callback lets parent components respond to time changes, which is useful for working with other UI parts or business rules - The
clearTimer
function makes sure we don’t leave any leftover intervals that could lead to memory leaks.
See the different examples in action on Code Sandbox.
Benefits and limitations of building your own countdown timer
Building your own countdown timer offers several advantages:
- It gives you complete control over the implementation
- No additional dependencies
- It offers a deep understanding of the timer's underlying mechanics
- You can customize every aspect of the timer’s behavior
However, it also comes with challenges:
- The need to handle various edge cases yourself
- Cross-browser compatibility considerations
- Performance optimization requirements
- Additional testing burden
Key features to consider in a React countdown library
When choosing a countdown library for your React project, it’s important to know what features set a good timing solution apart from a simple one. Let’s look at some key points to consider.
Customization options
Modern web applications need countdown displays that fit well with their designs.
The best countdown libraries offer plenty of ways to customize without losing performance or accuracy. Customization features include styling options based on components, display control with render props or functions, formatting, and localization for time units, animation hooks for transitions, and accessibility with proper ARIA attributes.
Precision and time management
A good countdown library should, obviously, keep time accurately.
While JavaScript's setTimeout
and setInterval
might work for simple timing, they can drift over long periods. High-quality countdown libraries use methods like syncing with server time, using algorithms to adjust for changes in system clocks, accurate time tracking with Performance.now()
, and batching updates to reduce performance issues when running multiple timers.
Performance optimization
Performance is crucial when using many countdown timers or making frequent updates.
The best countdown libraries use optimization techniques to keep things running smoothly. They focus on efficient update batching, shared timing loops for multiple countdowns, good memory management for finished or canceled timers, optimal use of React's reconciliation process, and smart throttling and debouncing for quick updates.
Ease of integration with React projects
How easily a countdown library fits into existing workflows can determine its success.
This includes everything from setup to ongoing maintenance. Important integration features include strong TypeScript support, clear error messages, thorough documentation, few peer dependencies, manageable bundle size, and support for server-side rendering and hydration.
Active maintenance and community support
The success of a countdown library over time depends a lot on community support and maintenance. Important factors include regular updates, prompt issue resolution, thorough test coverage, clear guidelines for contributions, and the size and engagement of the user community.
Now that you know what to look for when choosing a React countdown component library, let’s review some of the best countdown libraries available.
React Countdown
React Countdown is a popular countdown component library in the React ecosystem. It provides an easy-to-use API and strong customization features, making it versatile and reliable. The library works well for both simple and complex countdowns while keeping accurate time.
The library has several advantages that make it a good choice. First, it provides high accuracy because of its advanced timing system. Instead of just using setInterval
, it combines different methods to keep time accurately over long periods. This is especially important for long countdowns, such as for event or product launches. The renderer
prop gives you full control over how things look. It also provides an autoStart
control for automatic or manual start, utility functions such as zeroPad
, calcTimeDelta
, and formatTimeDelta
to assist in formatting and calculating time values, and an API (e.g., start
, pause
, and stop
) for controlling the countdown via a ref
. Here's an example of React Countdown’s basic usage:
import React from 'react';
import Countdown from 'react-countdown';
const Completionist = () => <span>Time's up!</span>;
const renderer = ({ hours, minutes, seconds, completed }) => {
if (completed) {
return <Completionist />;
} else {
return (
<span>
{hours}:{minutes}:{seconds}
</span>
);
}
};
const MyCountdown = () => (
<Countdown
date={Date.now() + 10000}
renderer={renderer}
/>
);
export default MyCountdown;
React Countdown Circle Timer
React/React Native Countdown Circle Timer is a lightweight React and React Native component that renders a countdown timer in a circular shape with color and progress animations. The library is known for its ability to create visually appealing circular progress indicators with minimal setup, offering color transitions and smooth animations that provide immediate user feedback about time progression, making it effective for timed quizzes, workout intervals, cooking timers, and meeting countdown displays.
The library also supports advanced customization through its comprehensive API; it uses a single request AnimationFrame
loop for efficient animation, supports smooth color transitions, and allows developers to define custom content within the circle's center. The library is compatible with both iOS and Android platforms, making it versatile for various applications.
The customization options include setting the countdown duration, defining color configurations for different time segments, managing the play state of the timer, and adjusting the stroke width and size of the SVG element.
Here's an example of React Countdown Circle Timer’s usage:
import { CountdownCircleTimer } from 'react-countdown-circle-timer';
const CircularTimer = () => {
return (
<CountdownCircleTimer
isPlaying
duration={60}
colors={['#004777', '#F7B801', '#A30000']}
colorsTime={[60, 30, 0]}
onComplete={() => {
// Return true to repeat the animation
return { shouldRepeat: true, delay: 1.5 }
}}
>
{({ remainingTime, color }) => (
<div style={{ color }}>
<div className="text-2xl font-bold">{remainingTime}</div>
<div className="text-sm">seconds</div>
</div>
)}
</CountdownCircleTimer>
);
};
You can check out all the customization options the library offers here.
react-flip-clock-countdown
react-flip-clock-countdown is a new library for React that creates a 3D animated flip-clock countdown component. It makes countdowns on your website more engaging and dynamic. react-flip-clock-countdown is good for situations where visual appeal matters. It is suitable for product launch countdowns, event marketing pages, interactive presentations, gaming interfaces, and timers for special offers.
react-flip-clock-countdown allows you to set a target date and time and customize labels for days, hours, minutes, and seconds. You can also control the visibility of labels and separators.
The library lets you style different parts of the countdown, adjust the flip animation duration, and choose whether the countdown hides after it finishes. Additionally, it provides event callbacks to handle actions when the countdown completes or at each tick, enabling you to show specific components or messages.
Here's an example of its usage:
import React from 'react';
import FlipClockCountdown from '@leenguyen/react-flip-clock-countdown';
import '@leenguyen/react-flip-clock-countdown/dist/index.css';
const App = () => {
return (
<FlipClockCountdown
to={new Date().getTime() + 24 * 3600 * 1000 + 5000} // 24 hours from now
labels={['Days', 'Hours', 'Minutes', 'Seconds']}
labelStyle={{ fontSize: 14, color: '#000' }}
digitBlockStyle={{ width: 40, height: 60, fontSize: 48 }}
separatorStyle={{ size: 12, color: '#000' }}
duration={0.5}
hideOnComplete={false}
onComplete={() => alert('Countdown completed!')}
>
<div>Countdown Complete!</div>
</FlipClockCountdown>
);
};
export default App;
You can check out all the customization props react-flip-clock-countdown offers here.
react-timer-hook
react-timer-hook takes a different approach by providing a collection of timing-related hooks rather than components. It was designed to manage timers, countdowns, and stopwatch functionalities within React components, which makes it quite flexible and ideal for developers who want to build custom timer interfaces.
The hook-based approach of react-timer-hook offers several advantages:
- You can easily add timing logic to existing components without changing their structure
- The hook takes care of complex state management while providing a simple interface
- You can efficiently use several timer instances within the same component
The React component offers three primary hooks: useTimer
, useStopwatch
, and useTime
, which manage countdown and stopwatch timers respectively. It provides comprehensive control methods like start
, pause
, resume
, and restart
for managing timer states. Customization options include autoStart
configuration, onExpire
callback function, and initial time offsets for stopwatches.
Here is how react-timer-hook separates logic from presentation:
import { useTimer } from 'react-timer-hook';
const Timer = ({ expiryTimestamp }) => {
const {
seconds,
minutes,
hours,
days,
isRunning,
start,
pause,
resume,
restart,
} = useTimer({
expiryTimestamp,
onExpire: () => console.warn('Timer expired!')
});
return (
<div className="timer-container">
<div className="timer-display">
<span>{days}</span>:<span>{hours}</span>:
<span>{minutes}</span>:<span>{seconds}</span>
</div>
<div className="timer-controls">
{isRunning ? (
<button onClick={pause}>Pause</button>
) : (
<button onClick={resume}>Resume</button>
)}
<button
onClick={() => {
// Restart timer with new expiry date
const time = new Date();
time.setSeconds(time.getSeconds() + 300);
restart(time);
}}
>
Restart (5 min)
</button>
</div>
</div>
);
};
use-timer
The use-timer library is similar to react-timer-hook in that it offers a simple way to create countdown timers by focusing on providing a lightweight, easy-to-use timer hook. Its simplicity makes it a great choice for basic timing needs. It also offers flexibility for custom uses.
This library works well for:
- Simple countdowns or countups
- Keeping the bundle size small
- Easily integrating with other components
- Quickly adding basic timing functions
Here's an example of use-timer’s basic usage:
import React from 'react';
import { useTimer } from 'use-timer';
const TimerComponent = () => {
const { time, start, pause, reset, status } = useTimer({
initialTime: 0,
timerType: 'INCREMENTAL',
interval: 1000,
step: 1,
autostart: false,
onTimeOver: () => console.log('Time is over'),
onTimeUpdate: (time) => console.log('Time updated:', time),
});
return (
<div>
<p>Elapsed time: {time} seconds</p>
<p>Status: {status}</p>
<button onClick={start}>Start</button>
<button onClick={pause}>Pause</button>
<button onClick={reset}>Reset</button>
</div>
);
};
export default TimerComponent;
Comparing the React countdown component libraries
Let’s take a look at all the React countdown components we’ve discussed to compare their features:
Characteristic | react-countdown | react-countdown-circle-timer | react-flip-clock-countdown | react-timer-hook | use-timer |
---|---|---|---|---|---|
API type | Component-based | Component-based | Component-based | Hook-based | Hook-based |
Animation support | Custom (via renderer) | Built-in SVG animations | Built-in 3D animation | Manual implementation | Manual implementation |
Styling flexibility | High (custom renderer) | Medium (predefined circle) | Medium | High (bring your own UI) | High (bring your own UI) |
Props/Configuration options | Extensive | Moderate | Moderate | Moderate | Basic |
Time format customization | Yes (via renderer) | Yes (via children) | Yes (via children) | Manual | Manual |
TypeScript support | Full | Full | Full | Full | Full |
SSR compatible | Yes | Yes | Yes | Yes | Yes |
Learning curve | Low to Medium | Low | Low | Low | Very Low |
Documentation quality | Excellent | Good | Good | Good | Good |
Active maintenance | High | High | Medium | Medium | Medium |
Browser support | Modern browsers + IE11 | Modern browsers | Modern browsers | Modern browsers | Modern browsers |
Best for | Complex countdown needs | Visual countdown experiences | Visual countdown experiences | Custom timer implementations | Simple timing requirements |
Conclusion
Using countdown timers in your React applications can boost user engagement and encourage desired actions. By learning how to create timers and exploring your options for third-party libraries, you can choose the best method for your project.
By focusing on performance, customization, ease of integration, and long-term maintenance, you can smoothly add countdown timers to your React projects, making the user experience more dynamic and engaging.
I hope this article has given you helpful tips about React countdown libraries. Let me know if you think of any React countdown component libraries I might have missed!
Get set up with LogRocket's modern React error tracking in minutes:
- Visit https://logrocket.com/signup/ to get an app ID.
- Install LogRocket via NPM or script tag.
LogRocket.init()
must be called client-side, not server-side.
NPM:
$ npm i --save logrocket
// Code:
import LogRocket from 'logrocket';
LogRocket.init('app/id');
Script Tag:
Add to your HTML:
<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>
3.(Optional) Install plugins for deeper integrations with your stack:
- Redux middleware
- ngrx middleware
- Vuex plugin
Top comments (0)