pwshub.com

How to Build a Countdown Timer with React – A Step-by-Step Guide

How to Build a Countdown Timer with React – A Step-by-Step Guide

In this tutorial, you will learn how to build a custom countdown timer to track events using React.js.

A countdown timer is a simple way to measure the time until an event happens. It counts down that time in reverse – like 5, 4, 3, 2, 1. It helps you manage the time leading up to upcoming events, product launches, or offers, and allows you to inform users about that timeline.

Table of Contents

  • 1. Set Up Your React App

  • 2. Create the Count Down Component

  • 3. Implement Time State Management and Functionality

  • 4. Create a Countdown Form

  • 5. Handle the Countdown Start, Stop, and Reset Functionality

  • 6. Format the Event Date and Time

  • 7. Display the CountDown Timer

  • 8. Styling the Countdown Timer Component

  • Conclusion

Prerequisites

You should have decent knowledge of HTML, CSS, and JavaScript to get the most out of this article.

Let's get started.

1. Set Up Your React App

First, you’ll need to create a React application if you don’t already have one ready to use. In this tutorial, I’m using Vite. Then change into the new project directory by running the following commands in your code editor:

npm create vite countdown-timer
cd countdown-timer

Run this command to start the app on the local server:

npm run dev

Now, you should see the project in your browser on https://localhost/3000.

2. Create the Count Down Component

In the src folder of your React app, create a components directory, and inside it, create a CountDown.jsx file.

/* components/CountDown.jsx */
import React from "react";
const CountdownTImer = () => {
  return (
    <div className="countdown-timer-container">
    </div>
  );
};
export default CountdownTimer;

3. Implement Time State Management and Functionality

Define the state variables using the useState hook. Update the CountDown.jsx file with the following code:

/* components/CountDown.jsx */
import React, { useState, useEffect } from "react";
const CountdownTimer = () => {
  const [eventName, setEventName] = useState("");
  const [eventDate, setEventDate] = useState("");
  const [countdownStarted, setCountdownStarted] = useState(false);
  const [timeRemaining, setTimeRemaining] = useState(0);
 return (
    <div className="countdown-timer-container">
    </div>
  );
};
export default CountdownTimer;

Here's a brief breakdown of the useState:

  • eventName: stores the name of the event for the countdown timer.

  • eventDate: stores the date of the event for the countdown timer.

  • countdownStarted: tracks whether the countdown timer has started.

  • timeRemaining: stores the remaining time in milliseconds for the countdown.

Now, we’ll implement the functionality of the countdown timer using the useEffect hook:

/* components/CountDown.jsx */
import React, { useState, useEffect } from "react";
const CountdownTimer = () => {
    // ...
  useEffect(() => {
    if (countdownStarted && eventDate) {
      const countdownInterval = setInterval(() => {
        const currentTime = new Date().getTime();
        const eventTime = new Date(eventDate).getTime();
        let remainingTime = eventTime - currentTime;
        if (remainingTime <= 0) {
          remainingTime = 0;
          clearInterval(countdownInterval);
          alert("Countdown complete!");
        }
        setTimeRemaining(remainingTime);
      }, 1000);
      return () => clearInterval(countdownInterval);
    }
  }, [countdownStarted, eventDate, timeRemaining]);
 return (
    <div className="countdown-timer-container">
    </div>
  );
};
export default CountdownTimer;

The useEffect hook runs whenever countdownStarted or eventDate changes. It sets up an interval that updates timeRemaining every second based on the current time and event time. If the remaining time becomes less than or equal to 0, it stops the interval and triggers the notification "Countdown complete!"

/* components/CountDown.jsx */
import React, { useState, useEffect } from "react";
const CountdownTimer = () => {
  // ...
  useEffect(() => {
    if (countdownStarted) {
      document.title = eventName;
    }
  }, [countdownStarted, eventName]);
 return (
    <div className="countdown-timer-container">
    </div>
  );
};
export default CountdownTimer;

Here, the useEffect hook runs whenever countdownStarted or eventName changes. It updates the countdown timer title to display the eventName when the countdown timer is started.

4. Create a Countdown Form

To have control over the countdown timer, you’ll need to create a form with two inputs for the name and date of the event. Then, add the following code:

/* components/CountDown.jsx */
import React from "react";
 // ...
   const handleSetCountdown = () => {
    setCountdownStarted(true);
  };
 return (
 <div className="countdown-timer-container">
      <h2 className="countdown-name">
        {countdownStarted ? eventName : "Countdown Timer"}
      </h2>
      {!countdownStarted ? (
        <form className="countdown-form">
          <label htmlFor="title">Event Name</label>
          <input
            name="title"
            type="text"
            placeholder="Enter event name"
            value={eventName}
            onChange={(e) => setEventName(e.target.value)}
          />
          <label htmlFor="date-picker">Event Date</label>
          <input
            name="date-picker"
            type="date"
            value={eventDate}
            onChange={(e) => setEventDate(e.target.value)}
            onClick={(e) => (e.target.type = "date")}
          />
          <button onClick={handleSetCountdown}>Start Countdown</button>
        </form>
      }
    </div>
  );
};
export default CountdownTimer;

Here's a brief breakdown of the useState:

  • eventName: stores the name of the event for the countdown timer.

  • countdown-name: displays the "Countdown Timer" by default or updates to the eventName entered once the countdown has started.

The form includes:

  • The input field with the name title and label Event Name update the eventName state value.

  • The input field with the name date-picker allow users to select a date and control the eventDate state value.

  • The button Start Countdown triggers the handleSetCountdown function when clicked to initiate the countdown.

5. Handle the Countdown Start, Stop, and Reset Functionality

Next, update the handleSetCountdown function to store the event name and date in the local storage using localStorage.setItem. localStorage is a web API that enables users to store data as key-value pairs persistently, even when the browser is closed or refreshed.

The code is as follows:

/* components/CountDown.jsx */
import React from "react";
 // ...
    const handleSetCountdown = () => {
    setCountdownStarted(true);
    localStorage.setItem("eventDate", eventDate);
    localStorage.setItem("eventName", eventName);
  };
 return (
 <div className="countdown-timer-container">
       // ...
 </div>
  );
};
export default CountdownTimer;

Now, create the handleStopCountdown and handleResetCountdown functions to stop the countdown timer by updating the countdownStarted state to false.

/* components/CountDown.jsx */
import React from "react";
 // ...
  const handleStopCountdown = () => {
    setCountdownStarted(false);
    setTimeRemaining(0);
  };
  const handleResetCountdown = () => {
    setCountdownStarted(false);
    setEventDate("");
    setEventName("");
    setTimeRemaining(0);
    localStorage.removeItem("eventDate");
    localStorage.removeItem("eventName");
  };
 return (
 <div className="countdown-timer-container">
       // ...
       <div className="control-buttons">
            <button onClick={handleStopCountdown}>Stop</button>
            <button onClick={handleResetCountdown}>Reset</button>
        </div>
 </div>
  );
};
export default CountdownTimer;

Here:

  • handleStopCountdown: resets the timeRemaining state to zero.

  • handleResetCountdown: resets the countdown timer to its initial state. It clears the remaining time states and removes the event date and event name from local storage using localStorage.removeItem().

6. Format the Event Date and Time

Let's convert date and time data into a readable format.

/* components/CountDown.jsx */
import React from "react";
 // ...
  const formatDate = (date) => {
    const options = { month: "long", day: "numeric", year: "numeric" };
    return new Date(date).toLocaleDateString("en-US", options);
  };
  const formatTime = (time) => {
    const seconds = Math.floor((time / 1000) % 60);
    const minutes = Math.floor((time / (1000 * 60)) % 60);
    const hours = Math.floor((time / (1000 * 60 * 60)) % 24);
    const days = Math.floor(time / (1000 * 60 * 60 * 24));
    return (
      <div className="countdown-display">
        <div className="countdown-value">
          {days.toString().padStart(2, "0")} <span>days</span>
        </div>
        <div className="countdown-value">
          {hours.toString().padStart(2, "0")} <span> hours</span>
        </div>
        <div className="countdown-value">
          {minutes.toString().padStart(2, "0")} <span>minutes</span>
        </div>
        <div className="countdown-value">
          {seconds.toString().padStart(2, "0")} <span>seconds</span>
        </div>
      </div>
    );
  };
 return (
 <div className="countdown-timer-container">
       // ...
 </div>
  );
};
export default CountdownTimer;

Here's a brief breakdown of the functions:

  • formatDate: formats the date input into a human-readable date string.

  • formatTime: takes a time in milliseconds as input and calculates the days, hours, minutes, and seconds of the timer. The .toString().padStart(2, "0") returns the formatted time as two characters by appending 0 at the beginning of the time only if the length of the number is less than 2.

Here are the complete contents of the CountDown.jsx file:

 /* components/CountDown.jsx */
import React, { useState, useEffect } from "react";
const CountdownTimer = () => {
  const [eventName, setEventName] = useState("");
  const [eventDate, setEventDate] = useState("");
  const [countdownStarted, setCountdownStarted] = useState(false);
  const [timeRemaining, setTimeRemaining] = useState(0);
  useEffect(() => {
    if (countdownStarted && eventDate) {
      const countdownInterval = setInterval(() => {
        const currentTime = new Date().getTime();
        const eventTime = new Date(eventDate).getTime();
        let remainingTime = eventTime - currentTime;
        if (remainingTime <= 0) {
          remainingTime = 0;
          clearInterval(countdownInterval);
          alert("Countdown complete!");
        }
        setTimeRemaining(remainingTime);
      }, 1000);
      return () => clearInterval(countdownInterval);
    }
  }, [countdownStarted, eventDate, timeRemaining]);
  useEffect(() => {
    if (countdownStarted) {
      document.title = eventName;
    }
  }, [countdownStarted, eventName]);
  const handleSetCountdown = () => {
    setCountdownStarted(true);
    localStorage.setItem("eventDate", eventDate);
    localStorage.setItem("eventName", eventName);
  };
  const handleStopCountdown = () => {
    setCountdownStarted(false);
    setTimeRemaining(0);
  };
  const handleResetCountdown = () => {
    setCountdownStarted(false);
    setEventDate("");
    setEventName("");
    setTimeRemaining(0);
    localStorage.removeItem("eventDate");
    localStorage.removeItem("eventName");
  };
  const formatDate = (date) => {
    const options = { month: "long", day: "numeric", year: "numeric" };
    return new Date(date).toLocaleDateString("en-US", options);
  };
  const formatTime = (time) => {
    const seconds = Math.floor((time / 1000) % 60);
    const minutes = Math.floor((time / (1000 * 60)) % 60);
    const hours = Math.floor((time / (1000 * 60 * 60)) % 24);
    const days = Math.floor(time / (1000 * 60 * 60 * 24));
    return (
      <div className="countdown-display">
        <div className="countdown-value">
          {days.toString().padStart(2, "0")} <span>days</span>
        </div>
        <div className="countdown-value">
          {hours.toString().padStart(2, "0")} <span> hours</span>
        </div>
        <div className="countdown-value">
          {minutes.toString().padStart(2, "0")} <span>minutes</span>
        </div>
        <div className="countdown-value">
          {seconds.toString().padStart(2, "0")} <span>seconds</span>
        </div>
      </div>
    );
  };
  return (
    <div className="countdown-timer-container">
      <h2 className="countdown-name">
        {countdownStarted ? eventName : "Countdown Timer"}
      </h2>
      <p className="countdown-date">
        {countdownStarted && formatDate(eventDate)}
      </p>
      {!countdownStarted ? (
        <form className="countdown-form">
          <label htmlFor="title">Event Name</label>
          <input
            name="title"
            type="text"
            placeholder="Enter event name"
            value={eventName}
            onChange={(e) => setEventName(e.target.value)}
          />
          <label htmlFor="date-picker">Event Date</label>
          <input
            name="date-picker"
            type="date"
            value={eventDate}
            onChange={(e) => setEventDate(e.target.value)}
            onClick={(e) => (e.target.type = "date")}
          />
          <button onClick={handleSetCountdown}>Start Countdown</button>
        </form>
      ) : (
        <>
          {formatTime(timeRemaining)}
          <div className="control-buttons">
            <button onClick={handleStopCountdown}>Stop</button>
            <button onClick={handleResetCountdown}>Reset</button>
          </div>
        </>
      )}
    </div>
  );
};
export default CountdownTimer;

7. Display the CountDown Timer

Import CountDownTimer in the App.jsx, replacing the default code with this:

 /* App.jsx */
import React from "react";
import CountdownTimer from "./components/CountDown";
function App() {
  return (
    <div className="App">
      <CountdownTimer />
    </div>
  );
}
export default App;

And that's it! Your countdown timer app should be rendered on localhost:3000 in the browser.

8. Styling the Countdown Timer Component

Lastly, update the index.css file in the same directory of your project by adding the following styles:

/* index.css */
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=Open+Sans:wght@400;500;700&display=swap");
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
body {
  background: url("./img/bg-img.jpg") top center;
  background-size: cover;
  font-family: "Inter", sans-serif;
  font-size: 1rem;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  margin: 0 auto;
  width: 100vw;
  height: 100vh;
}
.countdown-form {
  background-color: #f6f6f6;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 20px;
  width: 300px;
}
label {
  font-weight: bold;
  margin-bottom: 5px;
}
input {
  background-color: #d1f1ee;
  border: 1px solid #dfdfdf;
  outline: none;
  margin-bottom: 10px;
  padding: 10px;
  width: 100%;
}
button {
  background-color: #038a7f;
  border: none;
  color: #fff;
  cursor: pointer;
  outline: none;
  margin-top: 15px;
  text-transform: uppercase;
  width: 100%;
  height: 40px;
}
button:hover {
  background-color: #005a53;
}
.countdown-message {
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 24px;
}
.countdown-name {
  color: #fff;
  font-size: 1.5rem;
  margin-bottom: 14px;
  text-align: center;
}
.countdown-date {
  color: #eafbfa;
  margin: -22px 0 10px;
  text-align: center;
}
.countdown-display {
  display: flex;
  justify-content: space-around;
}
.countdown-value:not(span) {
  background-color: #2f5d6f;
  border-radius: 50%;
  color: #03d5c0;
  font-size: 46px;
  padding: 20px;
  margin: 0 5px;
  padding: 10px;
  text-align: center;
  width: 140px;
  height: 140px;
}
.countdown-value > span {
  display: block;
  color: #fff;
  font-size: 0.8rem;
  letter-spacing: 2px;
  text-transform: uppercase;
  margin-top: -25px;
}
.control-buttons {
  margin-top: 50px;
  text-align: center;
}
.control-buttons > button {
  background-color: #03b4a2;
  border: none;
  border-radius: 50%;
  color: #fff;
  cursor: pointer;
  font-size: 0.7rem;
  margin: 0 10px;
  width: 50px;
  height: 50px;
}
.control-buttons button:hover {
  background-color: #0b7c71;
}
@media only screen and (max-width: 600px) {
  .countdown-form {
    width: 90%;
    max-width: 300px;
  }
  input {
    width: 100%;
  }
  .countdown-name {
    margin-left: -20px;
  }
  .countdown-value:not(span) {
    font-size: 1.1rem;
    width: 80px;
    height: 80px;
  }
  .countdown-value > span {
    font-size: 0.7rem;
    margin-top: -15px;
  }
}

Congratulations, you’ve finished building your Countdown timer app!

Conclusion

In this article, you've learned how to build a basic React countdown timer app and how to work with the browser's local storage.

The code implemented in this article is accessible in this GitHub repository. To learn more about web development and technology, check out my blog or connect with me on X(Twitter) and LinkedIn.

Thank you for reading.

Source: freecodecamp.org

Related stories
1 month ago - Large Language Models are everywhere these days – think ChatGPT – but they have their fair share of challenges. One of the biggest challenges faced by LLMs is hallucination. This occurs when the model generates text that is factually...
1 month ago - This tutorial demonstrates how to build, integrate, and customize a bottom navigation bar in a Flutter app. The post How to build a bottom navigation bar in Flutter appeared first on LogRocket Blog.
1 week ago - Having a properly designed and comprehensive documentation site is important for any project. But creating good documentation can be challenging, and problems like poor user onboarding experience and increased support tickets can become...
5 days ago - Application development is a complex, multi-stage process, and it all begins with UI/UX design. Once the design phase is complete, the focus shifts to UI development, where tools like HTML, CSS, and JavaScript come into play. At a higher...
1 month ago - When you interact with web pages, a common task you’ll perform often is selecting text. Whether it's highlighting a section of a paragraph to copy, marking important parts of a document, or working with interactive features like...
Other stories
2 hours ago - Cloud hosting is a type of web hosting that hosts websites and applications on virtual servers provisioned across multiple geographic locations. It provides scalability, redundancy, dedicated resources, and superior control compared to...
3 hours ago - Ophir Wainer talks about how starting her career in product as a product leader influences her approach to the craft. The post Leader Spotlight: Jumping straight into product leadership, with Ophir Wainer appeared first on LogRocket Blog.
5 hours ago - AI image generator uses machine learning algorithms and deep learning models to create realistic or artistic images from text prompts or existing visuals. AI image generators can be used in various industries for tasks like creating...
12 hours ago - A 502 Bad Gateway error in Nginx may be a sign of more severe problems, so developers must know how to troubleshoot and resolve these errors.
14 hours ago - Here’s a thorough guide that covers everything you need to know to migrate your CommonJS project to ESM.