How to build a Countdown Timer with React Hooks

Web Development

In this tutorial, you will learn how to create a countdown timer with React js and React hooks like useState and useEffect. You will also install and use React Icons in order to create the Play, Pause and Stop button and of course the watch icon.

A countdown timer is a visual representation of the time remaining until an event or deadline. It is often used in games, quizzes, auctions, and other time-limited activities.

The React Countdown timer overview

Here is a step by step description of the code for the React countdown timer:

  1. The code starts by importing the necessary dependencies from the React library, as well as an additional Timer component and some icons from the react-icons/bs library. It then sets the background color of the body element to #282c34.
  2. Next, the CountdownTimer component is defined as a function component. It has several state variables: hours, minutes, seconds, milliseconds, and isRunning. The component also has a state variable called showEndScreen which is an object with two properties: show and message.
  3. The component has a useEffect hook that is called whenever the component renders or any of the state variables listed in the second argument of the hook (milliseconds, seconds, minutes, hours, isRunning, and showEndScreen.show) are updated.
  4. Within the useEffect hook, an interval is set using setInterval, which is called every 10 milliseconds. This interval counts down the timer by decreasing the value of milliseconds by 1 each time. If milliseconds reaches 0, it resets to 99 and seconds is decreased by 1. This process is repeated for minutes and hours.
  5. If the timer reaches 00:00:00:00, the showEndScreen state variable is updated to show the end screen, and the timer is reset using the resetTimer function.
  6. The startTimer function is called when the “Start” button is clicked. It checks if any time has been set for the timer, and if so, sets the isRunning state variable to true and hides the end screen by updating the showEndScreen state variable. If no time has been set, it displays an alert.
  7. The pauseTimer function is called when the “Pause” button is clicked. It sets the isRunning state variable to false, which causes the useEffect hook to stop the interval.
  8. The stopTimer function is called when the

Install React

To get started, we’ll need to set up a new React project. If you already have a project set up, you can skip this step. To create a new project, we’ll use the create-react-app command-line tool:

npx create-react-app countdown-timer
// or
npx create-react-app . // if folder already exists.

Next, we’ll install some additional dependencies that we’ll be using in our countdown timer. We’ll be using the react-icons/bs library for some buttons that we’ll be adding to the UI, so let’s install that first:

cd countdown-timer
npm start // Start the application.

App CSS

Copy and replace the css in your App.css with the code provided below

.App {
  text-align: center;
}

.App-logo {
  height: 40vmin;
  pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
  .App-logo {
    animation: App-logo-spin infinite 20s linear;
  }
}

.App-header {
  background-color: #282c34;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
  color: white;
}

.App-link {
  color: #61dafb;
}

@keyframes App-logo-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
@import url("https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,300;1,400;1,500;1,600;1,800&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Raleway:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap");
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  text-decoration: none;
  list-style-type: none;
}

/* Roboto Font */
body {
  font-family: "Roboto", sans-serif;
}

/*Container class*/
.container {
  max-width: 900px;
  margin: 0 auto;
}

.d-flex {
  display: flex;
  justify-content: center;
  align-items: center;
}
.d-flex.flex-column {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}

/*Title and Subtitle */
.title {
  font-size: 3.75rem;
  font-weight: 500;
  margin: 3rem 0;
}

.text-light {
  color: #f8f9fa;
}

/*Main Btn Class*/
.btn {
  margin: 0.25rem 0.125rem;
  padding: 0.35rem 1.25rem;
  font-family: inherit;
  font-weight: 500;
  font-size: 1rem;
  letter-spacing: 0.6px;
  border-radius: 0.3rem;
  color: #fff;
  background-color: #000;
  border: none;
  box-shadow: 0 2px 5px rgba(51, 51, 51, 0.3);
  transition: 0.3s ease;
}
.btn:hover,
.btn-accept:hover,
.btn-warning:hover {
  cursor: pointer;
  box-shadow: none;
  transition: 0.3s ease;
  background: rgba(0, 0, 0, 0.75);
}

/* Btn Types*/

.btn-danger {
  background-color: #dc3545;
  color: white;
}
.btn-danger:hover {
  background: rgba(220, 53, 69, 0.75);
}

.btn-warning {
  background-color: #ffc107;
  color: white;
  color: #000;
}
.btn-warning:hover {
  background: rgba(255, 193, 7, 0.75);
}

.btn-accept {
  background-color: #284ea7;
  color: white;
}
.btn-accept:hover {
  background: rgba(40, 78, 167, 0.75);
}

.btn-lg {
  margin: 0.3125rem 0.15625rem;
  padding: 0.4375rem 1.5625rem;
  font-size: 20px;
}

Create a countdown timer

With the dependencies installed, we can now start building our countdown timer. We’ll start by setting up the basic structure of our component. We’ll be creating a new file called Timer.js in the src directory of our project:

import React, { useState, useEffect } from "react";

function CountdownTimer() {
  const [hours, setHours] = useState(0);
  const [minutes, setMinutes] = useState(0);
  const [seconds, setSeconds] = useState(0);
  const [milliseconds, setMilliseconds] = useState()

We’ve defined several state variables for our timer: hours, minutes, seconds, and milliseconds. These variables will be used to store the current time left on the timer.

Next, we’ll add a useEffect hook to our component. This hook will be called whenever the component renders or any of the state variables listed in the second argument of the hook (in this case, milliseconds, seconds, minutes, and hours) are updated.

Within the useEffect hook, we’ll set an interval using setInterval. This interval will be called every 10 milliseconds, and it will count down the timer by decreasing the value of milliseconds by 1 each time. If milliseconds reaches 0, it will reset to 99 and seconds will be decreased by 1. This process will be repeated for minutes and hours.

Now that we have the basic structure of our countdown timer set up, we’ll add some buttons to control it. We’ll add “Start”, “Pause”, “Stop”, and “Reset” buttons to the UI. Let’s start with the “Start” button:

function startTimer() {
  if (hours !== 0 || minutes !== 0 || seconds !== 0 || milliseconds !== 0) {
    setIsRunning(true);
  } else {
    window.alert("Add Time.");
  }
}

return (
  <div>
    <button onClick={startTimer}>
      <BsFillPlayFill />
    </button>
  </div>
);

The “Start” button checks if any time has been set for the timer. If time has been set, it sets the isRunning state variable to true, which will start the interval in the useEffect hook. If no time has been set, it displays an alert.

Next, let’s add the “Pause” button:

function pauseTimer() {
  setIsRunning(false);
}

return (
  <div>
    <button onClick={startTimer}>
      <BsFillPlayFill />
    </button>
    <button onClick={pauseTimer}>
      <BsPauseFill />
    </button>
  </div>
);

The “Pause” button simply sets the isRunning state variable to false, which will pause the interval in the useEffect hook.

Next, let’s add the “Stop” and “Reset” buttons:

function stopTimer() {
  resetTimer();
}

function resetTimer() {
  setIsRunning(false);
  setMilliseconds(0);
  setSeconds(0);
  setMinutes(0);
  setHours(0);
}

return (
  <div>
    <button onClick={startTimer}>
      <BsFillPlayFill />
    </button>
    <button onClick={pauseTimer}>
      <BsPauseFill />
    </button>
    <button onClick={stopTimer}>
      <BsStopFill />
    </button>
    <button onClick={resetTimer}>Reset</button>
 

As I mentioned earlier, one potential feature we could add to our countdown timer is an “End Screen” that is displayed when the time runs out. To implement this, we’ll need to add a state variable called showEndScreen, which will store an object with a show property and a message property. We’ll also need to add a condition to our useEffect hook to check if the time has run out and set showEndScreen.show to true if it has.

Here’s how we can implement this feature:

const [showEndScreen, setShowEndScreen] = useState({
  show: false,
  message: "Happy coding in 2023!",
});

useEffect(() => {
  // existing code

  if (hours === 0 && minutes === 0 && seconds === 0 && milliseconds === 1) {
    setShowEndScreen({ ...showEndScreen, show: true });
    resetTimer();
  }
}, [milliseconds, seconds, minutes, hours, isRunning, showEndScreen.show]);

return (
  <div>
    {showEndScreen.show && (
      <h1 className="title text-light">{showEndScreen.message}</h1>
    )}
  </div>
);

Now, when the time runs out, the “End Screen” will be displayed with the message “Happy coding in 2023!”.

That’s it! We now have a fully functional countdown timer with the ability to set the time, start, pause, stop, and reset the timer, as well as display an “End Screen” when the time runs out.

Complete Code for the React Countdown timer

Countdown Timer component

You will also need to install reacticons to provide the Play, Pause and Stop icons.

import React, { useState, useEffect } from "react";
import Timer from "./Timer";

import { BsFillPlayFill, BsPauseFill, BsStopFill } from "react-icons/bs";

document.body.style.background = "#282c34";
export default function CountdownTimer() {
  const [hours, setHours] = useState(0);
  const [minutes, setMinutes] = useState(0);
  const [seconds, setSeconds] = useState(0);
  const [milliseconds, setMilliseconds] = useState(0);
  const [isRunning, setIsRunning] = useState(null);
  // End of Time

  const [showEndScreen, setShowEndScreen] = useState({
    show: false,
    message: "Happy coding in 2023",
  });
  useEffect(() => {
    let interval;
    if (isRunning) {
      interval = setInterval(() => {
        if (milliseconds > 0) {
          setMilliseconds((milliseconds) => milliseconds - 1);
        } else if (seconds > 0) {
          setSeconds((seconds) => seconds - 1);
          setMilliseconds(99);
        } else if (minutes > 0) {
          setMinutes((minutes) => minutes - 1);
          setSeconds(59);
          setMilliseconds(99);
        } else if (hours > 0) {
          setHours((hours) => hours - 1);
          setMinutes(59);
          setSeconds(59);
          setMilliseconds(99);
        }
      }, 10);
    }

    if (hours === 0 && minutes === 0 && seconds === 0 && milliseconds === 1) {
      setShowEndScreen({ ...showEndScreen, show: true });
      resetTimer();
    }
    return () => clearInterval(interval);
  }, [milliseconds, seconds, minutes, hours, isRunning, showEndScreen.show]);

  // Start Pause & Stop functions

  // Start
  function startTimer() {
    if (hours !== 0 || minutes !== 0 || seconds !== 0 || milliseconds !== 0) {
      setIsRunning(true);
      setShowEndScreen({ ...showEndScreen, show: false });
    } else {
      window.alert("Add Time.");
    }
  }

  // Pause
  function pauseTimer() {
    setIsRunning(false);
  }
  // Stop

  function stopTimer() {
    resetTimer();
    setShowEndScreen({ ...showEndScreen, show: false });
  }

  function resetTimer() {
    setIsRunning(false);
    setMilliseconds(0);
    setSeconds(0);
    setMinutes(0);
    setHours(0);
  }
  // Handlers

  const changeSeconds = (e) => {
    setSeconds(e.target.value);
  };
  const changeMinutes = (e) => {
    setMinutes(e.target.value);
  };
  const changeHours = (e) => {
    setHours(e.target.value);
  };
  return (
    <div>
      {showEndScreen.show && (
        <h1 className="title  text-light">{showEndScreen.message}</h1>
      )}
      <Timer
        milliseconds={milliseconds}
        seconds={seconds}
        minutes={minutes}
        hours={hours}
        changeSeconds={changeSeconds}
        changeMinutes={changeMinutes}
        changeHours={changeHours}
      />
      <br />
      {!isRunning && (
        <button className="btn btn-accept btn-lg" onClick={startTimer}>
          <BsFillPlayFill />
        </button>
      )}
      {isRunning && (
        <button className="btn btn-warning btn-lg" onClick={pauseTimer}>
          <BsPauseFill />
        </button>
      )}{" "}
      <button className="btn btn-danger btn-lg" onClick={stopTimer}>
        <BsStopFill />
      </button>
    </div>
  );
}

Timer component

import React from "react";
import { BsStopwatch } from "react-icons/bs";
import styled from "styled-components";

const TimerWrapper = styled.div`
  margin-top: 30vh;
  width: 600px;
  margin-left: auto;
  margin-right: auto;
  background-color: #222;
  color: #eee;
  border-radius: 5px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 5px 4px 6px rgba(0, 0, 0, 0.4);
  padding: 1rem 0;

  .stop-watch {
    font-size: 6rem;
    margin-right: 1rem;
  }
  label {
    margin-bottom: 0.5rem;
  }
  input {
    width: 100px;
    margin-right: 1rem;
    color: #282c34;
    outline: none;
    border: none;
    font-size: 4.5rem;
    font-weight: 600;
    text-align: center;
    padding: 0rem 0.5rem;
    border-radius: 5px;
  }
  input:hover {
    background-color: #928f8f;
  }
`;

export default function Timer({
  milliseconds,
  seconds,
  minutes,
  hours,
  changeSeconds,
  changeMinutes,
  changeHours,
}) {
  return (
    <TimerWrapper>
      <BsStopwatch className="stop-watch " />
      <div className="d-flex flex-column">
        <label>hh</label>
        <input value={hours} onChange={changeHours} />
      </div>{" "}
      <div className="d-flex flex-column">
        <label>mm</label>
        <input value={minutes} onChange={changeMinutes} />
      </div>{" "}
      <div className="d-flex flex-column">
        <label>ss</label>
        <input value={seconds} onChange={changeSeconds} />
      </div>{" "}
      <div className="d-flex flex-column">
        <label>ms</label>
        <input value={milliseconds} />
      </div>
    </TimerWrapper>
  );
}

Video Tutorial on Youtube

Check out the full detailed tutorial on my YouTube Channel for the React Countdown timer.

Want to become a web developer ?

Get complete web development courses on HTML, CSS , JavaScript, Bootstrap, Visual Studio Code, Responsive design and much more…

Love Podcast about web development?

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.