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 🙏

© 2025 – Pkg Stats / Ryan Hefner

repeating-countdown

v1.0.2

Published

Zero-dependency countdown utility with React hooks and components that resets on schedule with timezone-safe calculations

Readme

Repeating Countdown

A zero-dependency countdown utility with React hooks and components that resets on schedule with timezone-safe calculations.

Features

  • Zero Dependencies - No external dependencies for core functionality
  • 🔄 Repeating Countdowns - Automatically resets on schedule
  • 🌍 Timezone Safe - Proper timezone handling and server sync
  • ⚛️ React Ready - Custom hook and widget component
  • 🎨 Beautiful UI - Human-friendly display with progress bar
  • ⏸️ Pause/Resume - Full control over countdown state
  • 📱 Responsive - Works on all screen sizes
  • 🔧 TypeScript - Full type safety and IntelliSense support

Installation

npm install repeating-countdown

Quick Start

Basic Usage

import React from 'react';
import { CountdownWidget } from 'repeating-countdown';

function App() {
  const config = {
    duration: 3 * 24 * 60 * 60 * 1000, // 3 days
    autoReset: true
  };

  return <CountdownWidget config={config} />;
}

Using the Hook

import React from 'react';
import { useRepeatingCountdown } from 'repeating-countdown';

function CustomCountdown() {
  const {
    timeLeft,
    isPaused,
    isCompleted,
    progress,
    timeDisplay,
    start,
    pause,
    resume,
    reset
  } = useRepeatingCountdown({
    duration: 1000 * 60 * 60, // 1 hour
    autoReset: true,
    onComplete: () => console.log('Countdown completed!'),
    onReset: () => console.log('Countdown reset!')
  });

  return (
    <div>
      <h2>{timeDisplay.formatted}</h2>
      <div>Progress: {progress.toFixed(1)}%</div>
      <div>Status: {isCompleted ? 'Completed' : isPaused ? 'Paused' : 'Running'}</div>
      
      <button onClick={isPaused ? resume : pause}>
        {isPaused ? 'Start' : 'Pause'}
      </button>
      <button onClick={reset}>Reset</button>
    </div>
  );
}

API Reference

CountdownWidget

A pre-built React component with a beautiful UI.

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | config | CountdownConfig | - | Countdown configuration | | className | string | '' | Custom CSS class name | | showControls | boolean | true | Whether to show pause/resume/reset controls | | formatTime | (time: TimeDisplay) => string | Default formatter | Custom time formatting function | | showProgress | boolean | true | Whether to show progress bar | | progressColor | string | '#3b82f6' | Progress bar color |

Example

<CountdownWidget
  config={{
    duration: 1000 * 60 * 60, // 1 hour
    timezoneOffset: -300, // EST
    autoReset: true
  }}
  showControls={true}
  showProgress={true}
  progressColor="#10b981"
  className="my-countdown"
/>

useRepeatingCountdown Hook

A React hook for managing countdown state.

Parameters

  • config: CountdownConfig - Countdown configuration

Returns

{
  // State
  timeLeft: number;           // Time remaining in milliseconds
  isPaused: boolean;          // Whether countdown is paused
  isCompleted: boolean;       // Whether countdown has completed
  nextResetTime: Date;        // Time when countdown will reset
  progress: number;           // Progress percentage (0-100)
  timeDisplay: TimeDisplay;   // Formatted time display
  
  // Controls
  start: () => void;          // Start the countdown
  pause: () => void;          // Pause the countdown
  resume: () => void;         // Resume the countdown
  reset: () => void;          // Reset the countdown
  setDuration: (duration: number) => void; // Set new duration
}

CountdownConfig

Configuration object for countdown behavior.

interface CountdownConfig {
  duration: number;                    // Duration in milliseconds
  timezoneOffset?: number;             // Timezone offset in minutes (e.g., -300 for EST)
  serverOffset?: number;               // Server time offset in milliseconds
  autoReset?: boolean;                 // Whether to auto-reset when complete
  onComplete?: () => void;             // Callback when countdown reaches zero
  onReset?: () => void;                // Callback when countdown resets
}

TimeDisplay

Formatted time display object.

interface TimeDisplay {
  days: number;        // Days remaining
  hours: number;       // Hours remaining
  minutes: number;     // Minutes remaining
  seconds: number;     // Seconds remaining
  totalMs: number;     // Total milliseconds remaining
  formatted: string;   // Human-readable string (e.g., "2d 03h 45m 30s")
}

Advanced Examples

Server-Synced Countdown

import { useRepeatingCountdown } from 'repeating-countdown';

function ServerSyncedCountdown() {
  const config = {
    duration: 1000 * 60 * 60, // 1 hour
    serverOffset: 5000, // 5 seconds ahead of local time
    timezoneOffset: -300, // EST
    autoReset: true
  };

  const countdown = useRepeatingCountdown(config);
  
  return <div>{countdown.timeDisplay.formatted}</div>;
}

Custom Time Formatting

import { CountdownWidget } from 'repeating-countdown';

function CustomFormattedCountdown() {
  const formatTime = (time) => {
    if (time.days > 0) {
      return `${time.days} days, ${time.hours}h ${time.minutes}m`;
    }
    return `${time.hours}:${time.minutes.toString().padStart(2, '0')}:${time.seconds.toString().padStart(2, '0')}`;
  };

  return (
    <CountdownWidget
      config={{ duration: 1000 * 60 * 60 }}
      formatTime={formatTime}
    />
  );
}

Manual Reset Control

import { useRepeatingCountdown } from 'repeating-countdown';

function ManualResetCountdown() {
  const countdown = useRepeatingCountdown({
    duration: 1000 * 60 * 30, // 30 minutes
    autoReset: false,
    onComplete: () => {
      // Show notification or trigger action
      alert('Time\'s up!');
    }
  });

  return (
    <div>
      <h2>{countdown.timeDisplay.formatted}</h2>
      <div>Status: {countdown.isCompleted ? 'Completed' : countdown.isPaused ? 'Paused' : 'Running'}</div>
      
      {!countdown.isCompleted && (
        <button onClick={countdown.isPaused ? countdown.resume : countdown.pause}>
          {countdown.isPaused ? 'Start' : 'Pause'}
        </button>
      )}
      
      <button onClick={countdown.reset}>Reset</button>
    </div>
  );
}

Multiple Countdowns

import { useRepeatingCountdown } from 'repeating-countdown';

function MultipleCountdowns() {
  const workTimer = useRepeatingCountdown({
    duration: 1000 * 60 * 25, // 25 minutes (Pomodoro)
    autoReset: true
  });

  const breakTimer = useRepeatingCountdown({
    duration: 1000 * 60 * 5, // 5 minutes
    autoReset: true
  });

  return (
    <div>
      <div>
        <h3>Work Timer</h3>
        <div>{workTimer.timeDisplay.formatted}</div>
        <button onClick={workTimer.isPaused ? workTimer.resume : workTimer.pause}>
          {workTimer.isPaused ? 'Start' : 'Pause'}
        </button>
      </div>
      
      <div>
        <h3>Break Timer</h3>
        <div>{breakTimer.timeDisplay.formatted}</div>
        <button onClick={breakTimer.isPaused ? breakTimer.resume : breakTimer.pause}>
          {breakTimer.isPaused ? 'Start' : 'Pause'}
        </button>
      </div>
    </div>
  );
}

Utility Functions

The package also exports utility functions for custom implementations:

import {
  calculateNextResetTime,
  calculateTimeRemaining,
  formatTimeRemaining,
  formatTimeString,
  calculateProgress,
  shouldAutoReset,
  createDefaultConfig,
  validateConfig,
  getCurrentTime
} from 'repeating-countdown';

// Calculate next reset time
const nextReset = calculateNextResetTime(
  1000 * 60 * 60, // 1 hour
  -300, // EST timezone
  5000 // 5 seconds server offset
);

// Format time remaining
const timeLeft = 1000 * 60 * 30; // 30 minutes
const formatted = formatTimeRemaining(timeLeft);
console.log(formatted.formatted); // "30m 00s"

// Validate configuration
const config = { duration: 1000 * 60 * 60 };
const errors = validateConfig(config);
if (errors.length > 0) {
  console.error('Invalid config:', errors);
}

Styling

The CountdownWidget component comes with built-in styles but can be customized:

.repeating-countdown-widget {
  /* Custom styles */
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
}

.repeating-countdown-widget button:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}

Browser Support

  • Chrome 60+
  • Firefox 55+
  • Safari 12+
  • Edge 79+

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Changelog

1.0.0

  • Initial release
  • Core countdown functionality
  • React hook and widget component
  • Timezone and server sync support
  • TypeScript definitions
  • Comprehensive test suite