Let's build Launch Countdown Timer using Next.js & Tailwind CSS.
Tags: nextjs, tailwindcss, react, typescript
Published: 2022-05-10 11:10:17

---
title: Let's build Launch Countdown Timer using Next.js & Tailwind CSS.
published: true
date: 2022-05-01 07:04:00 UTC
tags: Nextjs,TailwindCSS,Reactjs,TypeScript
canonical_url: https://nextjsdev.com/lets-build-launch-countdown-timer-using-next-js-tailwind-css/
---

 ![Let's build Launch Countdown Timer using Next.js & Tailwind CSS.](https://nextjsdev.com/content/images/2022/04/nextjs-image-5.PNG)

Hello everyone,  I hope you all are doing well. Today again I am back with another exciting web dev project, which will help to learn some new web dev skills as a Front-end developer.

In this tutorial, I will show you how you can build a Launch Countdown Timer. It is one of the  Frontend Mentor challenge projects and our goal is to make it look like the design given by the Frontend Mentor.

Here is the link to the [FrontendMentor](https://www.frontendmentor.io/challenges/launch-countdown-timer-N0XkGfyz-) challenge that we will build.

So without any further talk, Let's start building it 🚀.

[🚀 Live Demo of the Project](https://launch-countdown-timer-raivikas.vercel.app/)

### Step-1 Initializing the Project  

Create a new next-js app with Tailwind CSS bootstrapped in it.

You can use this one-line command to create a new next.js app with TypeScript and Tailwind CSS.

<!--kg-card-begin: markdown-->
```bash
npx create-next-app -e with-tailwindcss my-project-name

```
<!--kg-card-end: markdown-->

You can name your project whatever you want, I will name it as **Launch countdown Timer.**

>💡 We are using TypeScript in this Project, so we have to explicitly define all the props, constants & function data types.

Now after creating the project open it in Vs Code or any IDE that you prefer.

Find the **index.tsx** file inside **pages** directory. and delete everything and paste the given code below.

<!--kg-card-begin: markdown-->
```jsx
import type { NextPage } from 'next'
import Head from 'next/head'

const Home: NextPage = () => {

return (
    <div className="flex min-h-screen flex-col items-center bg-[#1e1f29]">
      <Head>
        <title>Launch Countdown Timer</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

)}

export default Home;

```
<!--kg-card-end: markdown-->

After that visit the **globals.css** file inside the **styles** folder and import a google font link (Red Hat Font) at the top of the file inside it.

<!--kg-card-begin: markdown-->
```css
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800;900&family=Red+Hat+Text:wght@500;700&display=swap');

@tailwind base;
@tailwind components;
@tailwind utilities;

```
<!--kg-card-end: markdown-->

After that go to the **tailwindcss.config.js** file and we will add some custom font and animation that we will use later in the project.

<!--kg-card-begin: markdown-->
```js

module.exports = {
  content: [
    './pages/**/*.{js,ts,jsx,tsx}',
    './components/**/*.{js,ts,jsx,tsx}',
  ],
  theme: {
    extend: {
      animation: {
        flip:'flip 1s cubic-bezier(0, 0, 0.2, 1) infinite'
      },
      keyframes:{
        flip: {
          'from': { transform:'rotateX(0deg)', transformOrigin: '50% bottom ',},
          'to':{transform: 'rotateX(180deg)', transformOrigin: '50% bottom ',}
        }
      }
    },
       fontFamily: {
      'redhat':['Red Hat Text', 'sans-serif'],
      },
  plugins: [],
}

}

```
<!--kg-card-end: markdown-->

So, that was all for the first step, Now moving to the second step.

### Step-2 **Creating the Components**

Now, it is time to create some components that we are going to use in the app.

Create a new folder name **components** in the root of the directory  and create 5 files inside it.

1. **Header.tsx**
2. **Footer.tsx**
3. **NumberBox.tsx**
4. **TimerContainer.tsx**
5. **TimerInput.tsx**

After creating the file we will start adding the code to each file one by one.

**Inside Header.tsx**

<!--kg-card-begin: markdown-->
```jsx
import React from 'react'

interface msgProps {
  message: string,
};

export const Header = ({ message }: msgProps) => {
  return (
    <header className="mx-auto mt-2">
      <h1 className="text-2xl mt-8 md:text-4xl font-bold font-redhat text-rose-500 font-bold text-center mx-auto ">
        {message ? message : "WE'RE LAUNCHING SOON"}
      </h1>
    </header>
  )

```
<!--kg-card-end: markdown-->

**Inside Footer.tsx**

<!--kg-card-begin: markdown-->
```jsx
import React from 'react'

export const Footer = () => {
  return (
    <div className="mx-auto w-full ">
      <div className=" w-full flex mt-10 md:mt-40 space-x-6 justify-center items-center ">
        <a href="https://facebook.com/raivikas200" target="_blank">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" className="h-7 w-7 md:h-10 md:w-10 hover:cursor-pointer" fill="rgb(244 63 94)">
            <path d="M504 256C504 119 393 8 256 8S8 119 8 256c0 123.78 90.69 226.38 209.25 245V327.69h-63V256h63v-54.64c0-62.15 37-96.48 93.67-96.48 27.14 0 55.52 4.84 55.52 4.84v61h-31.28c-30.8 0-40.41 19.12-40.41 38.73V256h68.78l-11 71.69h-57.78V501C413.31 482.38 504 379.78 504 256z" />
          </svg>
        </a>
        <a href="https://github.com/raiv200" target="_blank">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" className="h-7 w-7 md:h-10 md:w-10 hover:cursor-pointer" fill="rgb(244 63 94)">
            <path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z" />
          </svg>
        </a>

        <a href="https://twitter.com/raivikas200" target="_blank">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" className="h-7 w-7 md:h-10 md:w-10 hover:cursor-pointer" fill="rgb(244 63 94) ">
            <path d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z" />
          </svg>
        </a>
      </div>
    </div>
  )
}

```
<!--kg-card-end: markdown-->

**Inside NumberBox.tsx**

<!--kg-card-begin: markdown-->
```jsx
import React from 'react'

interface numProp {
    num: string | number,
    unit: string,
    flip: boolean,
};

export const NumberBox = ({ num, unit, flip }: numProp) => {
    return (
        <div className="flex flex-col items-center mt-4 px-2">
            <div className=" relative bg-transparent flex flex-col items-center justify-center rounded-lg w-32 h-32 text-2xl md:text-4xl mt-4 ">
                <div className="rounded-t-lg rounded-b-lg bg-[#343650] w-full h-full"></div>

                <div className="text-5xl absolute text-rose-500 z-10 font-bold font-redhat md:text-7xl font-mono ">
                    {num}
                </div>

                <div className=" rounded-b-lg rounded-t-lg bg-[#2c2e3f] w-full h-full"></div>

                <div className={`absolute w-full h-1/2 top-0 rounded-t-lg z-5 ${flip ? 'animate-flip bg-rose-200' : 'bg-transparent'}`}></div>
                {/* Two Small Dots */}
                <div className="absolute -right-1 top-[60px] rounded-full w-[12px] h-[12px] bg-[#1e1f29]"></div>
                <div className="absolute -left-1 top-[60px] rounded-full w-[12px] h-[12px] bg-[#1e1f29]" ></div>

            </div>
            <p className="text-lg mt-3 font-semibold text-rose-200 md:text-2xl ">
                {unit}
            </p>
        </div>
    )
}

```
<!--kg-card-end: markdown-->

**Inside TimerContainer.tsx**

<!--kg-card-begin: markdown-->
```jsx
import React from 'react'
import { NumberBox } from './NumberBox'

interface timeProps{
  days: number | string,
  hours:number | string ,
  minutes:number | string,
  seconds:number | string,
}

export const TimerContainer = ({days, hours, minutes ,seconds }: timeProps) => {

  let daysFlip = false;
  let hoursFlip = false;
  let minutesFlip = false;
  let secondsFlip = true;

 if (seconds <=0 && minutes <=0 && hours <=0 && days <=0){
   daysFlip = false;
   hoursFlip = false;
   minutesFlip = false;
   secondsFlip = false;
 }

 if(seconds == 0){
   if( minutes !=0){
    seconds=59;
   }
   
   secondsFlip = false;
   minutesFlip = true;
 }
 if (minutes == 0 ){
    if( hours !=0){
      minutes=59;
    }
   
   minutesFlip = false;
   hoursFlip = true;
 }

 if( hours == 0){
   hoursFlip = false;
   if(days !=0){
     daysFlip = true;
   }
   
 }

 

   if(days <10){
     days="0"+days
   }

   if(hours <10){
     hours="0"+hours
   }

   if(minutes <10){
     minutes="0"+minutes
   }

   if(seconds < 10){
     seconds="0"+seconds
     
   }
  
    return (

     <div className=" mt-2 md:mt-20 rounded-xl">
       <div className="grid grid-cols-2 gap-4 py-6 px-10 md:flex md:items-center md:justify-between md:mt-2 rounded-xl md:px-6 md:py-8 ">
            <NumberBox num={days } unit="Days" flip={daysFlip} />
            <span className=" hidden text-5xl -mt-8 md:inline-block md:text-7xl font-normal text-gray-50 ">:</span>
            <NumberBox num={hours } unit="Hours" flip={hoursFlip} />
            <span className="hidden text-5xl -mt-8 md:inline-block md:text-7xl font-normal text-gray-50 ">:</span>
            <NumberBox num={minutes} unit="Minutes" flip={minutesFlip}/>
            <span className="hidden text-5xl -mt-8 md:inline-block md:text-7xl font-normal text-gray-50 ">:</span>
            <NumberBox num={seconds} unit="Seconds" flip={secondsFlip} />
        </div>
   
      </div>
    )
}

```
<!--kg-card-end: markdown-->

**Inside TimerInput.tsx**

<!--kg-card-begin: markdown-->
```jsx
import React from 'react'

interface inputProps {
    value:number,
    handleClick() : void,
    handleChange(e:any) : void,
}

export const TimerInput = ({value , handleClick ,handleChange}: inputProps) => {
  return (
    <div className="z-6 mx-auto space-y-4 flex flex-col md:flex-row justify-center items-center md:space-y-0">
        <input 
        className="text-xl md:text-2xl font-redhat outline-none px-2 py-1 w-40 rounded-lg mr-4 "
        name="Timer Input"
        type="number" 
        placeholder="Enter No. of Days" 
        value={value}
        onChange={handleChange}
        min={0}
        />

      <button onClick={handleClick} className="bg-rose-300 text-xl font-semibold font-redhat px-4 py-2 md:text-xl rounded-xl text-rose-500 hover:bg-rose-500 hover:text-rose-100 transition duration-300 ease-in"> Set Value</button>
    </div>
  )
}

```
<!--kg-card-end: markdown-->

After creating the components and adding the above code, Import all the components inside the index.tsx file.

You will notice that you are getting some errors in the files that the **props** you are using are not defined.

It is because of TypeScript since it is a strongly typed language, each and everything has to be defined.

But don't worry as soon as we complete the code the error will go.

The **index.tsx** file will look like this now:

<!--kg-card-begin: markdown-->
```jsx
import type { NextPage } from 'next'
import Head from 'next/head'
import { TimerContainer } from '../components/TimerContainer'
import { Footer } from '../components/Footer'
import { Header } from '../components/Header'
import { TimerInput } from '../components/TimerInput'

const Home: NextPage = () => {

return (
    
<div className="flex min-h-screen flex-col items-center bg-[#1e1f29]">
      <Head>
        <title>Launch Countdown Timer</title>
        <link rel="icon" href="/favicon.ico" />

      </Head>

      <Header message={message} />

      <TimerContainer
        days={days}
        hours={hours}
        minutes={minutes}
        seconds={seconds}
      />
      <TimerInput value={newTime} handleClick={handleClick} handleChange={handleChange} />

      <Footer />
    </div>

)}

export default Home;

```
<!--kg-card-end: markdown-->

As you can see, I am passing various props to the components.

Now, in the next step, we are going to create some variables using the **useState** Hook and write the logic of the timer.

### Step-3 Code Logic for the Countdown Timer

Open the index.tsx file and inside the Home component create some variable.

And after that, we will add the code logic for the countdown timer inside Home Component above the return statement.

**Inside Home Component (index.tsx):**

<!--kg-card-begin: markdown-->
```jsx
  const [newTime, setNewTime] = useState<number>(0)
  const [days, setDays] = useState<number>(0);
  const [hours, setHours] = useState<number>(0);
  const [minutes, setMinutes] = useState<number>(0);
  const [seconds, setSeconds] = useState<number>(0);
  const [message, setMessage] = useState<string>("");

 const timeToDays = time * 60 * 60 * 24 * 1000;

 let countDownDate = new Date().getTime() + timeToDays;

```
<!--kg-card-end: markdown-->

💡

**Make sure to import the useState hook and also useEffect Hook from react.**

Now below the above code, we will use the **setInterval** function inside the **useEffect** hook, which will update the time every second.

<!--kg-card-begin: markdown-->
```jsx
useEffect(() => {

    var updateTime = setInterval(() => {
      var now = new Date().getTime();

      var difference = countDownDate - now;

      var newDays = Math.floor(difference / (1000 * 60 * 60 * 24));
      var newHours = Math.floor((difference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
      var newMinutes = Math.floor((difference % (1000 * 60 * 60)) / (1000 * 60));
      var newSeconds = Math.floor((difference % (1000 * 60)) / 1000);

      setDays(newDays);
      setHours(newHours);
      setMinutes(newMinutes);
      setSeconds(newSeconds);

      if (difference <= 0) {
        clearInterval(updateTime);
        setMessage("The Launch Has Started");
        setDays(0);
        setHours(0);
        setMinutes(0);
        setSeconds(0);
      }
    })

    return () => {
      clearInterval(updateTime);
    }

  }, [time]);

```
<!--kg-card-end: markdown-->

As you can see we have also used the **clearInterval()** function which will clear or stop the **setInerval()** function as soon as the countdown time reaches zero.

The step-3 is done but two more things are remaining.

We have to create two functions handleClick() and handleChange() function which will be used to set the **countdown timer** as per the **user-entered input.**

### Step-4 Creating the two functions handleClick() & handlechange().

After the end of the useEffect function, create two functions below it **handleChange()** and **handleClick()** like this:

<!--kg-card-begin: markdown-->
```jsx
const handleClick = () => {

    setTime(newTime);
    console.log(time);
    setNewTime(0);
  };

  const handleChange = (e: any) => {
    let inputTime = e.target.value;
    setNewTime(inputTime);

  };

```
<!--kg-card-end: markdown-->

Now for the final step, we will add the flip animation.

### Step-5 Adding the Flip Animation

The flip animation will be added conditionally, I have already added the code for the animation in advance inside the **TimerContainer.tsx** and the **NumberBox.tsx** components in **Step-2**.

You can take a look at the code and try to understand it on your own as a challenge.

It's not that difficult to understand the conditional logic that I have used to conditionally show the Flips.

Congratulations 🎉 🥳 !!!

Everything is done and you have successfully created awesome projects.

Now open the terminal inside VS code and start the development server by running the **npm run dev** command.

And visit **localhost:3000** and your application is ready.

### Conclusion

Hope you were able to build this **Launch Countdown Timer**.  Feel free to follow me on [Twitter](https://twitter.com/raivikas200) and share this if you like this project 😉.

I hope you like this project and enjoyed building it, I would appreciate ✌️ it if you could share this blog post.

If you think that this was helpful and then please do consider visiting my blog website [nextjsdev.com](https://nextjsdev.com/) and do follow me on **Twitter** and connect with me on **LinkedIn**.

If you were stuck somewhere and not able to find the solution you can check out my completed [Github Repo](https://github.com/raiv200/Launch-Countdown-Timer) here.

Thanks for your time to read this project, if you like this please share it on Twitter and Facebook or any other social media and tag me there.

I will see you in my next blog ✌️. Till then take care and keep building projects.

Some Useful Link:

[Next.js and Tailwind Installation Docs](https://tailwindcss.com/docs/guides/nextjs)

[Github link for the project](https://github.com/raiv200/Launch-Countdown-Timer)

**Connect with me:**

[Twitter Profile](https://twitter.com/raivikas200)

[LinkedIn Profile](https://www.linkedin.com/in/raivikas200/)

[GitHub Profile](https://github.com/raiv200)

[Facebook Profile](https://facebook.com/raivikas200)