DEV Community

Cover image for How to Create a Circular Countdown (React Component)
Sergei Ipatev
Sergei Ipatev

Posted on • Edited on

4

How to Create a Circular Countdown (React Component)

Once, I needed to create a countdown component in the form of a circle, so I decided to share its implementation:

Stack: ReactJS, TypeScript, SCSS (modules)

The first step is to create a component(CircleCountDown.tsx):

import { useEffect, useState, FC, useMemo } from 'react';
import styles from './CircleCountDown.module.scss';

interface CircleCountDownProps {
  time: number;
  size: number;
  stroke: string;
  strokeWidth: number;
  onComplete?: VoidFunction;
  strokeLinecap?: 'butt' | 'round' | 'square' | 'inherit' | undefined;
}

const CircleCountDown: FC<CircleCountDownProps> = ({
  time,
  size,
  stroke,
  onComplete,
  strokeWidth,
  strokeLinecap = 'round',
}) => {
  const radius = size / 2;
  const milliseconds = time * 1000;
  const circumference = size * Math.PI;

  const [countdown, setCountdown] = useState(milliseconds);

  const seconds = (countdown / 1000).toFixed();

  const strokeDashoffset = circumference - (countdown / milliseconds) * circumference;

  useEffect(() => {
    const interval = setInterval(() => {
      if (countdown > 0) {
        setCountdown(countdown - 10);
      } else {
        clearInterval(interval);
        onComplete && onComplete();
      }
    }, 10);
    return () => clearInterval(interval);
  }, [countdown]);

  return (
    <div className={styles.root}>
      <label className={styles.seconds}>{seconds}</label>
      <div className={styles.countDownContainer}>
        <svg className={styles.svg} width={size} height={size}>
          <circle
            fill="none"
            r={radius}
            cx={radius}
            cy={radius}
            stroke={stroke}
            strokeWidth={strokeWidth}
            strokeLinecap={strokeLinecap}
            strokeDasharray={circumference}
            strokeDashoffset={strokeDashoffset}
          />
        </svg>
      </div>
    </div>
  );
};

export default CircleCountDown;
Enter fullscreen mode Exit fullscreen mode

The second step is to create the styles(CircleCountDown.module.scss):

@mixin font-serif($name, $size: false, $color: false, $fontStyle: false, $lineHeight: false, $weight: false) {
  font-family: $name, sans-serif;
  @if $size {
    font-size: $size;
  }
  @if $color {
    color: $color;
  }
  @if $fontStyle {
    font-style: $fontStyle;
  }
  @if $lineHeight {
    line-height: $lineHeight;
  }
  @if $weight {
    font-weight: $weight;
  }
}

.root {
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
}

.seconds {
  position: absolute;
  @include font-serif('Roboto', 16px, false, normal, 20px, 500);
}

.svg {
  transform: scale(-1, 1);
  overflow: visible;
}

.countDownContainer {
  transform: rotate(90deg);
}
Enter fullscreen mode Exit fullscreen mode

The third step is to use it in (App.tsx):

import React from 'react';
import CircleDownCircle from "./components/CircleCountDown/CircleCountDown";

function App() {
  return (
    <CircleDownCircle time={114} size={45} stroke="#000" strokeWidth={2} />
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

As you can see, we have the onComplete method. When the timer finishes, onComplete will be called.
That's all! I hope you like this example. 😊

Source code: GitHub

Live Demo:

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay