useStopwatch

React hook to create a stopwatch functionality.

import { useState, useEffect } from 'react'

const addLeadingZero = (digit: number): string => {
  let timeStr = ''

  digit % 10 === digit ? (timeStr += `0${digit}`) : (timeStr += `${digit}`)

  return timeStr
}

interface Stopwatch {
  current: string
  isPaused: boolean
  isOver: boolean
  currentDays: number
  currentHours: number
  currentMinutes: number
  currentSeconds: number
  elapsedSeconds: number
  pause: () => void
  play: () => void
  reset: () => void
  togglePause: () => void
}

export const useStopwatch = (): Stopwatch => {
  const [time, setTime] = useState({
    days: 0,
    hours: 0,
    minutes: 0,
    seconds: 0,
  })
  const [paused, setPaused] = useState(false)
  const divider = ':'
  const [isOver, setIsOver] = useState(false)

  useEffect(() => {
    if (paused) {
      return
    }

    const interval = setInterval(() => {
      setTime((prev) => {
        let d = prev.days
        let h = prev.hours
        let m = prev.minutes
        let s = prev.seconds

        if (s + 1 >= 60) {
          s = 0
          if (m + 1 >= 60) {
            m = 0
            if (h + 1 >= 24) {
              h = 0
              d++
            } else {
              h++
            }
          } else {
            m++
          }
        } else {
          s++
        }

        return { days: d, hours: h, minutes: m, seconds: s }
      })
    }, 1000)

    return () => clearInterval(interval)
  }, [time, paused])

  return {
    current: `${addLeadingZero(time.days)}${divider}${addLeadingZero(
      time.hours,
    )}${divider}${addLeadingZero(time.minutes)}${divider}${addLeadingZero(
      time.seconds,
    )}`,
    isPaused: paused,
    isOver,
    currentDays: time.days,
    currentHours: time.hours,
    currentMinutes: time.minutes,
    currentSeconds: time.seconds,
    elapsedSeconds:
      time.days * 86400 + time.hours * 3600 + time.minutes * 60 + time.seconds,
    pause: () => setPaused(true),
    play: () => setPaused(false),
    reset: () => {
      setIsOver(false)
      setTime({ days: 0, hours: 0, minutes: 0, seconds: 0 })
    },
    togglePause: () => {
      setPaused(!paused)
    },
  }
}

React 16.8 or higher

  • Name
    current
    Type
    string
    Description

    The current value of the stopwatch.

  • Name
    isPaused
    Type
    boolean
    Description

    Represents whether the stopwatch is paused (true) or not (false).

  • Name
    isOver
    Type
    boolean
    Description

    Represents whether the stopwatch has ended paused (true) or not (false).

  • Name
    currentDays
    Type
    number
    Description

    The current value of days on the stopwatch.

  • Name
    currentHours
    Type
    number
    Description

    The current value of hours on the stopwatch.

  • Name
    currentMinutes
    Type
    number
    Description

    The current value of minutes on the stopwatch.

  • Name
    currentSeconds
    Type
    number
    Description

    The current value of the seconds in the stopwatch.

  • Name
    elapsedSeconds
    Type
    number
    Description

    The number of seconds that have passed since the start of the stopwatch.

  • Name
    pause
    Type
    function
    Description

    Pause the stopwatch.

  • Name
    play
    Type
    function
    Description

    Play the stopwatch.

  • Name
    reset
    Type
    function
    Description

    Reset the stopwatch.

  • Name
    togglePause
    Type
    function
    Description

    Toggle between pausing and playing the stopwatch.

import { useStopwatch } from './hooks/useStopwatch'

const App = () => {
  const {
    current,
    isPaused,
    isOver,
    currentDays,
    currentHours,
    currentMinutes,
    currentSeconds,
    elapsedSeconds,
    pause,
    play,
    reset,
    togglePause,
  } = useStopwatch()

  return (
    <div>
      <p>Counter value: {current}</p>
      <p>Is the counter paused? {isPaused ? 'Yes' : 'No'}</p>
      <p>Has the counter over? {isOver ? 'Yes' : 'No'}</p>
      <p>Current days: {currentDays}</p>
      <p>Current hours: {currentHours}</p>
      <p>Current minutes: {currentMinutes}</p>
      <p>Current seconds: {currentSeconds}</p>
      <p>Elapsed seconds: {elapsedSeconds}</p>
      <button onClick={pause}>Pause</button>
      <button onClick={play}>Play</button>
      <button onClick={reset}>Reset</button>
      <button onClick={togglePause}>Toggle Pause</button>
    </div>
  )
}

export default App

Here are some use cases where this React hook is useful:

  • Tracking time elapsed during a coding session in a developer productivity tool
  • Creating a stopwatch feature for tracking workout durations in a fitness tracking app