Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

react-countdown-circle-timer

vydimitrov96.8kMIT3.2.1TypeScript support: included

Lightweight React countdown timer component with color and progress animation based on SVG

react, countdown, timer, circle, clock, SVG

readme

React Countdown Circle Timer

npm npm Codecov npm bundle size

React countdown timer component in a circle shape with color and progress animation.

Features

:zap: Performance optimized with single requestAnimationFrame loop to animate color and progress
:rainbow: Transition between colors during the countdown
:european_castle: Fully customizable content in the center of the circle

Install

yarn add react-countdown-circle-timer

Usage

Component

Check the CodeSandbox demo to get started.

import { CountdownCircleTimer } from 'react-countdown-circle-timer'

const UrgeWithPleasureComponent = () => (
  <CountdownCircleTimer
    isPlaying
    duration={7}
    colors={['#004777', '#F7B801', '#A30000', '#A30000']}
    colorsTime={[7, 5, 2, 0]}
  >
    {({ remainingTime }) => remainingTime}
  </CountdownCircleTimer>
)

Hook

The package exports a hook useCountdown, which accepts the same props as the component and returns all props needed to render your own circle.

import { useCountdown } from 'react-countdown-circle-timer'

const {
  path,
  pathLength,
  stroke,
  strokeDashoffset,
  remainingTime,
  elapsedTime,
  size,
  strokeWidth,
} = useCountdown({ isPlaying: true, duration: 7, colors: '#abc' })

Props

Prop Name Type Default Description
duration number required Countdown duration in seconds
colors string | string[] required colors prop is either:
- Single valid color in any format or URL to a gradient
- Array of colors in HEX format. At least 2 colors should be provided
colorsTime number[] - Indicates the time when a color should switch to the next color. The first number is the countdown duration and the last one is 0 or goal. Works only when colors is an array of HEX colors
isPlaying boolean false Play or pause animation
initialRemainingTime number - Set the initial remaining time if it is different than the duration
updateInterval number 0 Update interval in seconds. Determines how often the timer updates. When set to 0 the value will update on each key frame
size number 180 Width and height of the SVG element
strokeWidth number 12 Path stroke width
trailStrokeWidth number strokeWidth Trail stroke width
strokeLinecap round | square | butt round Path stroke line cap
rotation clockwise | counterclockwise clockwise Progress path rotation direction
isGrowing boolean false Indicated if the progress path should be growing instead of shrinking
trailColor string #d9d9d9 Circle trail color - takes any valid color format
isSmoothColorTransition boolean true Indicates if the colors should smoothly transition to the next color
children (props: { remainingTime: number, elapsedTime: number, color: string }) => ReactNode - Render function to customize the time/content in the center of the circle
onComplete (totalElapsedTime: number) => void | { shouldRepeat: boolean, delay?: number, newInitialRemainingTime?: number } - On animation complete event handler
onUpdate (remainingTime: number) => void - On remaining time update event handler

Browser support

The component and hook support all modern browsers targeting ES6. Internet Explorer (IE) is not longer supported.

Recipes

Changing duration prop

Once the component is mounted the duration prop can be changed the the timer will respect the new duration. In case the new duration is bigger than the previous one then the timer will continue to the new duration. In case the new duration is smaller then the previous one then the timer will be over. If you want to restart the timer when the duration changes then pass a new key prop to CountdownCircleTimer component and the timer will start over with the new values provided.

Restart timer at any given time

Pass a key prop to CountdownCircleTimer and change the key when the timer should be restarted. Check this demo to find out one possible implementation.

Repeat timer when countdown is completed

Return an object from onComplete handler, which indicates if the animation should be repeated. Example:

const UrgeWithPleasureComponent = () => (
  <CountdownCircleTimer
    isPlaying
    duration={10}
    colors="#A30000"
    onComplete={() => {
      // do your stuff here
      return { shouldRepeat: true, delay: 1.5 } // repeat animation in 1.5 seconds
    }}
  />
)

Set the initial remaining time different then the duration provided

Pass the remaining time to initialRemainingTime prop. Example:

const UrgeWithPleasureComponent = () => (
  <CountdownCircleTimer
    isPlaying
    duration={60}
    initialRemainingTime={15}
    colors="#A30000"
  />
)

In the example above, the countdown will start at 15 seconds (one quarter before it's done) and it will animate for 15 seconds until it reaches 0.

Time formatting functions

children prop of CountdownCircleTimer component will receive as a prop remainingTime in seconds. Below are a few functions that can be used to get different time formatting:

  • Format mm:ss (minutes and seconds)
const children = ({ remainingTime }) => {
  const minutes = Math.floor(remainingTime / 60)
  const seconds = remainingTime % 60

  return `${minutes}:${seconds}`
}
  • Format hh:mm:ss (hours, minutes and seconds)
const children = ({ remainingTime }) => {
  const hours = Math.floor(remainingTime / 3600)
  const minutes = Math.floor((remainingTime % 3600) / 60)
  const seconds = remainingTime % 60

  return `${hours}:${minutes}:${seconds}`
}

Add a11y support

  • Wrapper the timer in an element and add the following attribute aria-label={your-aria-label}
  • Add the following element with role="timer" to your children function that will make the screen reader read the remaining time while the timer is running.
const children = ({ remainingTime }) => (
  <div role="timer" aria-live="assertive">
    {remainingTime} seconds
  </div>
)

Add gradient

Define the SVG gradient outside the Timer component and pass the gradient ID to the Timer component as a single color:

<svg>
  <defs>
    <linearGradient id="your-unique-id" x1="1" y1="0" x2="0" y2="0">
      <stop offset="5%" stopColor="gold" />
      <stop offset="95%" stopColor="red" />
    </linearGradient>
  </defs>
</svg>
<CountdownCircleTimer colors="url(#your-unique-id)">
  {({ remainingTime }) => remainingTime}
</CountdownCircleTimer>

Slide down time animation

Check the CodeSandbox demo to find out how you can implement it yourself

Days, hours, minutes, seconds countdown

Check the CodeSandbox demo for one possible solution

changelog

Change Log

All notable changes to this project will be documented in this file. See Conventional Commits for commit guidelines.

3.2.1

Fix

  • Build packages before publish to include changes from v.3.2.0

3.2.0

Features

  • Add isGrowing prop that indicates if the progress should grow instead of shrink. Thanks @bonqus!

Fix

  • Workflow action to run tests and publish results

3.1.0

Features

  • Add viewBox prop to the svg element for the wen and mobile packages. The viewBox prop will make the countdown timer responsive and it will adjust its size according to the element outside.

3.0.9

Fix

  • Fix for #193. This is fix for the issue where types for the hook are referenced by the shared package, which is not exported. To solve the problem now we copy the types from the shared package and add them to each package when bundling the code. This is not the best solution but it is the simplest. Once we have a good way to bundle types from monorepo this can be changed.

3.0.8

Fix

  • Draft a new release since the previous one did not include the type update

3.0.7

Fix

  • Extend strokeLinecap to support "butt" option

3.0.6

Fix

  • Fix README on both packages and sync up versions

3.0.5

Fix(mobile)

  • The animating path is visible when the animation is over on Android. To fix it we check if the elapsed time is equal the duration

3.0.4

Fix

  • Upgrade use-elapsed-time to fix an issue where the newStartAt was not respected

3.0.3

Fix

  • Accept a new argument from onComplete that can control the initialRemainingTime

3.0.2

Fix

3.0.1

Fix

  • Add README for web package so it shows in NPM

3.0.0

Improvements

  • The Web and Mobile packages are now written in TypeScript
  • PNPM is now used as package manager as well as to handle the monorepo
  • Webpack is replaced by Esbuild to bundle the code and run dev server
  • PropTypes are no longer required as peerDependencies. The component relays on the TypeScript types
  • New prop updateInterval is added to control how often the timer should be updated
  • New prop colorsTime is added to handle the times when a color should switch to the next color. This was part of the colors prop before
  • New prop isSmoothColorTransition indicates if the colors should smoothly transition to the next color or just change the color when the time comes
  • New event handler onUpdate will fire every time the time changes
  • Reduce overall bundle size and provide module export for the Web package

Breaking changes

  • IE is no longer supported
  • colors prop now is either: Single color in any valid color format or URL to a gradient; Array of colors in HEX format. At least 2 colors should be provided.
  • Gradient is no longer supported out of the box and isLinearGradient and gradientUniqueKey are now deprecated. The gradient can be set from outside of the component. Please refer to the recipes section.
  • ariaLabel and renderAriaTime are also deprecated. Refer to the recipes section to check how this can be implemented.
  • children prop now accepts only a render function and it does not take a React component as a children

React Native (mobile) changes

  • The Mobile package does not rely on AnimatedPath to animate the SVG path but instead it uses the animation event loop from use-elapsed-time. Thus now both packages - web and mobile share the same core logic to animate the path. Performance comparison shows that the later approach is much more performant.

2.5.4 (2021-08-29)

Note: Version bump only for package countdown-circle-timer-monorepo

2.5.3 (2021-05-13)

Bug Fixes

  • web: upgrade use-elapsed-time to 2.1.8 (9eb0166)

2.5.2 (2021-04-28)

Bug Fixes

  • mobile: return elapsedTime in seconds fixes #119 (0a2ac54)

2.5.1 (2021-03-25)

Bug Fixes

  • mobile: react-native ignore pattern in tests (16cf3cc)

2.5.0 (2021-02-15)

Bug Fixes

  • change from or to null operator and other fixes suggested in PR. (2e79ed3)
  • the issue of stroke overflowing when trailStrokeWidth is different from strokeWidth (b3d3aab)

Features

  • Add inner and outer stroke width prop to timer. (bdc35a7)
  • add snapshots for trailStrokeWidth for web also (ce12b54)
  • add test for trailStrokeWidth mobile (67542b1)

2.4.0 (2021-01-15)

Bug Fixes

  • web: put the browser list to the correct file (2e4d510)

Features

  • web: add browser list to package.json (a88c8c8)

2.3.12 (2021-01-14)

Bug Fixes

2.3.11 (2021-01-10)

Note: Version bump only for package countdown-circle-timer-monorepo

2.3.10 (2020-10-11)

Bug Fixes

  • mobile: wrong onComplete type fixes#56 (07b2a6c)

2.3.9 (2020-09-06)

Bug Fixes

  • mobile: prevent running onComplete while in finished state fixes #51 (0ea726d)

2.3.8 (2020-08-29)

Bug Fixes

  • mobile: add correct dependencies in hooks (9d77921)
  • minutes formatting in recipes on Readme page (acab4c8)

Features

  • add eslint plugin:react-hooks (288320f)

2.3.7 (2020-07-24)

Note: Configure Github action to release using Lerna. No changes on component code.

2.3.6 (2020-07-22)

Note: Configure Github action to release using Lerna. No changes on component code.

2.3.5 (2020-07-22)

Note: Configure Github action to release using Lerna. No changes on component code.

2.3.4 (2020-07-22)

Note: Configure Github action to release using Lerna. No changes on component code.

2.3.3 (2020-07-21)

Note: An attempt to configure Lerna to create Github releases. No changes on component code.

2.3.2 (2020-07-21)

Note: An attempt to configure Lerna to create Github releases. No changes on component code.

2.3.1 (2020-07-21)

Note: An attempt to configure Lerna to create Github releases. No changes on component code.

2.3.0 (2020-07-21)

Features

  • web, mobile: allow passing string as a single color to colors prop (f5ba08c)

2.0.0

Implemented enhancements:

  • Make the repo monorepo handled by Lerna
  • Breaking changes:
    • Remove the old startAt prop. The recommended prop to use is initialRemainingTime instead.
    • Remove margin styles from the wrapper and all svg styles
    • durationSeconds is renamed to just duration. The duration is still in seconds
    • renderTime prop is now deprecated. React children should be used instead. The children prop accepts a render function or a component where remainingTime and elapsedTime are passed as props.
    • renderAriaTime prop will receive as an argument an object with the remainingTime and elapsedTime

1.2.1 (April 8th, 2020)

Implemented enhancements:

  • Refactor internal logic to prep the code for monorepo
  • Remove path.getTotalLength() dependency and add logic to calculate the path length
  • Improve test coverage
  • Add Prettier

Bug fixes:

  • Fix an issue in Firefox where the progress bar does not get to 0

1.1.1 (February 23th, 2020)

Implemented enhancements:

  • Add GitHub workflow to report test coverage and build status
  • Add a new badge for weekly downloads

1.1.0 (February 1st, 2020)

Implemented enhancements:

  • Replace startAt with initialRemainingTime. Using startAt in the context of countdown is confusing
  • startAt can be used until the next major release for backward compatibility
  • initialRemainingTime sets the initial remaining time when the countdown starts

1.0.6 (January 12th, 2020)

Implemented enhancements:

  • Add TypeScript type definitions

1.0.5 (December 22nd, 2019)

Implemented enhancements:

  • Add test coverage

1.0.4 (December 3rd, 2019)

Implemented enhancements:

  • Add a11y support by exposing two additional props ariaLabel and renderAriaTime

1.0.3 (November 16th, 2019)

Implemented enhancements:

  • Replace custom hook that handles animation loop with useElapsedTime
  • Add Changelog file