npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@usefy/use-timer

v0.0.38

Published

A React hook for managing countdown timers with precision and control

Readme


Overview

@usefy/use-timer is a feature-rich countdown timer hook for React applications. It provides accurate time tracking with drift compensation, multiple time formats, flexible controls, and smart render optimization.

Part of the @usefy ecosystem — a collection of production-ready React hooks designed for modern applications.

Why use-timer?

  • Accurate Timing — Uses performance.now() with drift compensation for precise countdowns
  • Smart Render Optimization — Only re-renders when the formatted time actually changes
  • Multiple Formats — Built-in formats (MM:SS, HH:MM:SS, mm:ss.SSS) or custom formatters
  • Full Control — Start, pause, stop, reset, restart, and toggle controls
  • Time Manipulation — Add, subtract, or set time dynamically during countdown
  • Background Tab Support — Visibility API integration for accurate time when switching tabs
  • Loop Mode — Automatically restart the timer when it completes
  • TypeScript First — Full type safety with comprehensive type definitions
  • Zero Dependencies — Pure React implementation
  • Well Tested — 123 tests with comprehensive coverage

Installation

# npm
npm install @usefy/use-timer

# yarn
yarn add @usefy/use-timer

# pnpm
pnpm add @usefy/use-timer

Peer Dependencies

This package requires React 18 or 19:

{
  "peerDependencies": {
    "react": "^18.0.0 || ^19.0.0"
  }
}

Quick Start

import { useTimer, ms } from "@usefy/use-timer";

function Timer() {
  const timer = useTimer(ms.minutes(5), { format: "MM:SS" });

  return (
    <div>
      <p>{timer.time}</p>
      <button onClick={timer.toggle}>
        {timer.isRunning ? "Pause" : "Start"}
      </button>
      <button onClick={timer.reset}>Reset</button>
    </div>
  );
}

API Reference

useTimer(initialTimeMs, options?)

A hook that manages countdown timer state with comprehensive controls.

Parameters

| Parameter | Type | Description | | --------------- | ----------------- | -------------------------------------- | | initialTimeMs | number | Initial countdown time in milliseconds | | options | UseTimerOptions | Optional configuration object |

Options

| Option | Type | Default | Description | | ------------ | ----------------------------- | --------- | ------------------------------------------------ | | interval | number | 1 | Update interval in milliseconds | | format | TimeFormat \| TimeFormatter | "MM:SS" | Time display format or custom formatter function | | autoStart | boolean | false | Start timer automatically on mount | | loop | boolean | false | Restart timer automatically when it completes | | useRAF | boolean | false | Use requestAnimationFrame instead of interval | | onComplete | () => void | - | Callback when timer reaches 0 | | onTick | (remaining: number) => void | - | Callback on each tick with remaining time | | onStart | () => void | - | Callback when timer starts | | onPause | () => void | - | Callback when timer is paused | | onReset | () => void | - | Callback when timer is reset | | onStop | () => void | - | Callback when timer is stopped |

Time Formats

| Format | Example | Description | | ---------------- | -------------- | ---------------------------------- | | "MM:SS" | 05:30 | Minutes and seconds | | "HH:MM:SS" | 01:05:30 | Hours, minutes, and seconds | | "SS" | 330 | Total seconds | | "mm:ss.SSS" | 05:30.123 | Minutes, seconds, and milliseconds | | "HH:MM:SS.SSS" | 01:05:30.123 | Full format with milliseconds | | Custom function | Any string | (ms: number) => string |

Returns UseTimerReturn

| Property | Type | Description | | -------------- | ---------------------- | ------------------------------------------------------- | | time | string | Formatted time string (e.g., "05:30") | | progress | number | Progress percentage (0-100) | | status | TimerStatus | Current status: idle, running, paused, finished | | isRunning | boolean | Whether timer is running | | isPaused | boolean | Whether timer is paused | | isFinished | boolean | Whether timer has finished | | isIdle | boolean | Whether timer is idle | | start | () => void | Start the timer | | pause | () => void | Pause the timer | | stop | () => void | Stop timer (keep current time) | | reset | () => void | Reset to initial time | | restart | () => void | Reset and immediately start | | toggle | () => void | Toggle between start/pause | | addTime | (ms: number) => void | Add time to the timer | | subtractTime | (ms: number) => void | Subtract time from the timer | | setTime | (ms: number) => void | Set timer to a specific value |

ms Helper Object

A utility object for converting time units to milliseconds.

import { ms } from "@usefy/use-timer";

ms.seconds(30); // 30000
ms.minutes(5); // 300000
ms.hours(1); // 3600000

// Combine them
ms.hours(1) + ms.minutes(30); // 1h 30m = 5400000

Examples

Basic Countdown Timer

import { useTimer, ms } from "@usefy/use-timer";

function CountdownTimer() {
  const timer = useTimer(ms.minutes(5), {
    format: "MM:SS",
    onComplete: () => alert("Time's up!"),
  });

  return (
    <div>
      <div className="time-display">{timer.time}</div>
      <div className="controls">
        <button onClick={timer.toggle}>
          {timer.isRunning ? "⏸ Pause" : "▶ Start"}
        </button>
        <button onClick={timer.reset}>↺ Reset</button>
      </div>
    </div>
  );
}

Precision Timer with Milliseconds

import { useTimer, ms } from "@usefy/use-timer";

function PrecisionTimer() {
  const timer = useTimer(ms.seconds(10), {
    format: "mm:ss.SSS",
    interval: 1, // Update every 1ms
  });

  return (
    <div>
      <div className="precision-display">{timer.time}</div>
      <button onClick={timer.toggle}>
        {timer.isRunning ? "Pause" : "Start"}
      </button>
    </div>
  );
}

Auto-Start Timer

import { useTimer, ms } from "@usefy/use-timer";

function AutoStartTimer() {
  const timer = useTimer(ms.seconds(30), {
    autoStart: true,
    format: "MM:SS",
    onComplete: () => console.log("Completed!"),
  });

  return <div>{timer.time}</div>;
}

Looping Timer

import { useTimer, ms } from "@usefy/use-timer";

function LoopingTimer() {
  const timer = useTimer(ms.seconds(10), {
    loop: true,
    format: "MM:SS",
    onComplete: () => console.log("Loop completed!"),
  });

  return (
    <div>
      <p>{timer.time}</p>
      <button onClick={timer.toggle}>
        {timer.isRunning ? "Pause" : "Start"}
      </button>
    </div>
  );
}

Time Manipulation

import { useTimer, ms } from "@usefy/use-timer";

function ManipulableTimer() {
  const timer = useTimer(ms.minutes(1), { format: "MM:SS" });

  return (
    <div>
      <p>{timer.time}</p>
      <div className="controls">
        <button onClick={timer.toggle}>
          {timer.isRunning ? "Pause" : "Start"}
        </button>
        <button onClick={() => timer.addTime(ms.seconds(10))}>+10s</button>
        <button onClick={() => timer.subtractTime(ms.seconds(10))}>-10s</button>
        <button onClick={timer.reset}>Reset</button>
      </div>
    </div>
  );
}

Progress Bar

import { useTimer, ms } from "@usefy/use-timer";

function TimerWithProgress() {
  const timer = useTimer(ms.minutes(1), { format: "MM:SS" });

  return (
    <div>
      <p>{timer.time}</p>
      <div className="progress-bar">
        <div
          className="progress-fill"
          style={{ width: `${100 - timer.progress}%` }}
        />
      </div>
      <button onClick={timer.toggle}>
        {timer.isRunning ? "Pause" : "Start"}
      </button>
    </div>
  );
}

Custom Formatter

import { useTimer, ms } from "@usefy/use-timer";

function CustomFormattedTimer() {
  const timer = useTimer(ms.hours(2) + ms.minutes(30), {
    format: (ms) => {
      const hours = Math.floor(ms / 3600000);
      const minutes = Math.floor((ms % 3600000) / 60000);
      const seconds = Math.floor((ms % 60000) / 1000);
      return `${hours}h ${minutes}m ${seconds}s`;
    },
  });

  return (
    <div>
      <p>{timer.time}</p> {/* "2h 30m 0s" */}
      <button onClick={timer.toggle}>
        {timer.isRunning ? "Pause" : "Start"}
      </button>
    </div>
  );
}

Kitchen Timer with Presets

import { useTimer, ms } from "@usefy/use-timer";

function KitchenTimer() {
  const timer = useTimer(ms.minutes(5), {
    format: "MM:SS",
    onComplete: () => playAlarm(),
  });

  const presets = [1, 3, 5, 10, 15, 30];

  return (
    <div>
      <div className="presets">
        {presets.map((min) => (
          <button key={min} onClick={() => timer.setTime(ms.minutes(min))}>
            {min}m
          </button>
        ))}
      </div>
      <p>{timer.time}</p>
      <button onClick={timer.toggle}>
        {timer.isRunning ? "Pause" : "Start"}
      </button>
    </div>
  );
}

Render Optimization

The hook is optimized to only trigger re-renders when the formatted time changes, not on every interval tick. This means:

| Format | Re-render frequency | | ----------- | ------------------- | | HH:MM | Every minute | | MM:SS | Every second | | MM:SS.S | Every 100ms | | MM:SS.SS | Every 10ms | | MM:SS.SSS | Every 1ms |

This optimization is automatic — no configuration needed!


TypeScript

This hook is written in TypeScript and exports comprehensive type definitions.

import {
  useTimer,
  ms,
  type UseTimerOptions,
  type UseTimerReturn,
  type TimerStatus,
  type TimeFormat,
  type TimeFormatter,
} from "@usefy/use-timer";

// Full type inference
const timer: UseTimerReturn = useTimer(60000, {
  format: "MM:SS",
  onComplete: () => console.log("Done!"),
});

// Status is typed as union
const status: TimerStatus = timer.status; // "idle" | "running" | "paused" | "finished"

Performance

  • Stable Function References — All control functions are memoized with useCallback
  • Smart Re-renders — Only re-renders when formatted time changes
  • Drift Compensation — Uses performance.now() for accurate timing
  • Background Tab Handling — Visibility API integration prevents timer drift when tab is inactive
const { start, pause, toggle, reset } = useTimer(60000);

// These references remain stable across renders
useEffect(() => {
  // Safe to use as dependencies
}, [start, pause, toggle, reset]);

Testing

This package maintains comprehensive test coverage to ensure reliability and stability.

Test Coverage

📊 View Detailed Coverage Report (GitHub Pages)

Test Files

  • useTimer.test.ts — 67 tests for hook behavior
  • formatUtils.test.ts — 24 tests for time formatting
  • timeUtils.test.ts — 32 tests for time utilities

Total: 123 tests


License

MIT © mirunamu

This package is part of the usefy monorepo.