Building a Countdown Component for Vue with Composables

/
158

In this tutorial, we'll build a simple countdown component for Vue using composable functions. We'll start by creating a useCountdown composable that calculates the time left until a specified future date, and then we'll use this composable in a Countdown component to display the remaining time in hours, minutes, and seconds — like this:

23h 59m 59s
until
3/29/2024, 8:55:00 AM

CREATING THE useCountdown COMPOSABLE

Our useCountdown composable function will take a futureDate parameter and return an object containing the time left until that date. To accomplish this, we'll first define an interface for our TimeLeft object that contains a Ref for each unit of time we want to display:

import { Ref } from "@vue/runtime-core"

interface TimeLeft {
  seconds: Ref<number>
  minutes: Ref<number>
  hours: Ref<number>
  days: Ref<number>
  months: Ref<number>
  years: Ref<number>
}

Next, we'll define our useCountdown function, which will create a timeLeft object containing the Refs defined above. It will then start a setInterval timer that calls a calculateTimeLeft function every second to update the timeLeft object. If the remaining time is less than 0, we'll stop the timer using clearInterval and return the timeLeft object.


export function useCountdown(futureDate: Date): {
  timeLeft: TimeLeft,
  clearCountdown: Function
} {

  const timeLeft: TimeLeft = ( {
    seconds: ref(0),
    minutes: ref(0),
    hours: ref(0),
    days: ref(0),
    months: ref(0),
    years: ref(0)
  } )

  let interval: string | number | NodeJS.Timer | undefined
  const clearCountdown = () => {
    clearInterval(interval)
  }

  const calculateTimeLeft = () => {

    const timeRemaining = Date.parse(futureDate.toString()) - Date.now()
    if (timeRemaining < 0) {
      clearCountdown()
      return timeLeft
    }

    timeLeft.seconds.value = Math.floor(( timeRemaining / 1000 ) % 60)
    timeLeft.minutes.value = Math.floor(( timeRemaining / 1000 / 60 ) % 60)
    timeLeft.hours.value = Math.floor(( timeRemaining / ( 1000 * 60 * 60 ) ) % 24)
    timeLeft.days.value = Math.floor(timeRemaining / ( 1000 * 60 * 60 * 24 ))
    timeLeft.months.value = Math.floor(timeLeft.days.value / 30)
    timeLeft.years.value = Math.floor(timeLeft.months.value / 12)

    return timeLeft
  }

  interval = setInterval(calculateTimeLeft, 1000)
  calculateTimeLeft()

  return {
    timeLeft,
    clearCountdown
  }
}

BUILDING THE Countdown COMPONENT

Now that we have our useCountdown composable function, we can use it in a Countdown component to display the remaining time until a specified date. We'll create a futureDate variable to hold the date we're counting down to, and then call useCountdown with that variable to create a countdown to that date and time.

We'll use the onUnmounted lifecycle hook to call clearCountdown when the component is unmounted to avoid memory leaks.

Finally, we render the remaining time along with the countdown end date using simple HTML and CSS. You can customize the styles of the component by modifying the CSS in the style section.

<script lang="ts" setup>
import { useCountdown } from "~/composables/useCountdown"

let futureDate = new Date()
futureDate.setTime(futureDate.getTime() + ( 60 * 60 * 24 * 1000 ))

const {timeLeft, clearCountdown} = useCountdown(futureDate)
const {hours, minutes, seconds, days, years} = timeLeft

onUnmounted(() => {
  clearCountdown()
})
</script>

<template>
  <div class="countdown">
    <div class="countdown__remaining">
      {{ hours }}h {{ minutes }}m {{ seconds }}s
    </div>
    <div class="countdown__until">
      until
    </div>
    <div class="countdown__date">{{ futureDate.toLocaleString() }}</div>
  </div>
</template>

<style lang="scss" scoped>
.countdown {
  text-align: center;
  margin-top: 1rem;
  margin-bottom: 1rem;

  &__remaining {
    font-weight: bold;
    font-size: 2rem;
  }

  &__until {
    text-transform: uppercase;
    margin: .25rem auto;
  }

  &__date {
    font-size: 1.5rem;
  }
}
</style>

All done!

In this tutorial, we've built a countdown component for Vue using composable functions. We started by creating a useCountdown composable that calculates the time left until a specified future date, and then we used this composable in a Countdown component to display the remaining time in hours, minutes, and seconds.

By using composable functions, we were able to separate the logic for calculating the time left from the presentation of the countdown component, making our code more modular and easier to maintain.

I hope this tutorial has been helpful in demonstrating the power of composable functions in Vue, and how they can simplify complex logic in your components. If you have any questions or feedback, please feel free to leave a comment below.


TOY MODE
π