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

react-timedown-ui

v1.1.0

Published

A headless, flexible timedown timer hook and component for React applications

Readme

⏰ React Timedown

A flexible, headless countdown timer hook and component for React applications. Built with TypeScript and designed for maximum flexibility.

npm version npm downloads CI codecov license

📚 Documentation

Features

  • 🎯 Headless Design - Complete control over your UI
  • Flexible Input - Accepts seconds, minutes, hours, days, or Date objects
  • 🎨 Custom Formatting - Multiple built-in formats or create your own
  • 🔄 Full Control - Start, pause, resume, and reset functionality
  • 📦 TypeScript Ready - Written in TypeScript with full type definitions
  • 🪶 Lightweight - Zero dependencies (except React)
  • Well Tested - Comprehensive test coverage

Installation

npm install react-timedown-ui

or

yarn add react-timedown-ui

Quick Start

Using the Hook

import { useTimedown } from 'react-timedown-ui';

function MyTimedown() {
  const { time, start, pause, reset, isRunning } = useTimedown({
    seconds: 60, // 60 seconds - CLEAR AND EXPLICIT!
  });

  return (
    <div>
      <div>{time.formatted}</div>
      <button onClick={start}>Start</button>
      <button onClick={pause}>Pause</button>
      <button onClick={reset}>Reset</button>
    </div>
  );
}

Using the Component (Render Props)

import { Timedown } from 'react-timedown-ui';

function MyTimedown() {
  return (
    <Timedown seconds={60}>
      {({ time, start, pause, reset, isRunning }) => (
        <div>
          <div>{time.formatted}</div>
          <button onClick={start}>Start</button>
          <button onClick={pause}>Pause</button>
          <button onClick={reset}>Reset</button>
        </div>
      )}
    </Timedown>
  );
}

Usage Examples

Hook Examples

Simple Usage - Seconds

// Most straightforward - just pass seconds
const { time, start } = useTimedown({
  seconds: 60, // Clear: 60 seconds
  autoStart: false,
});

Simple Usage - Minutes

// Or use minutes prop
const { time, pause } = useTimedown({
  minutes: 5, // Clear: 5 minutes
  autoStart: true,
});

Simple Usage - Hours

// Or hours
const { time } = useTimedown({
  hours: 2, // Clear: 2 hours
  autoStart: true,
});

Combined Time Units

// Combine multiple units - they add up!
const { time } = useTimedown({
  hours: 1,
  minutes: 30,
  seconds: 45,
  autoStart: true,
});
// Total: 5445 seconds

Using Duration Object

// For complex durations, use the duration prop
const { time } = useTimedown({
  duration: {
    days: 2,
    hours: 5,
    minutes: 30,
    seconds: 15,
  },
  autoStart: true,
});

Countdown Until a Specific Date

const newYear = new Date('2026-01-01T00:00:00');

const { time } = useTimedown({
  until: newYear, // Countdown until New Year
  autoStart: true,
});

With Callbacks

const { time } = useTimedown({
  seconds: 60,
  onComplete: () => {
    console.log('Timedown completed!');
    // Show notification, play sound, etc.
  },
  onTick: (time) => {
    console.log('Current time:', time);
    // Update document title, etc.
  },
});

Custom Format

const { time } = useTimedown({
  seconds: 3665, // 1 hour, 1 minute, 5 seconds
  format: 'custom',
  customFormat: (time) => {
    return `${time.hours}h ${time.minutes}m ${time.seconds}s`;
  },
});

Different Built-in Formats

// Format: HH:MM:SS (default)
const timedown1 = useTimedown({ seconds: 3665 });
// Result: 01:01:05

// Format: MM:SS
const timedown2 = useTimedown({ 
  seconds: 3665,
  format: 'MM:SS' 
});
// Result: 61:05

// Format: DD:HH:MM:SS
const timedown3 = useTimedown({ 
  duration: { days: 2, hours: 5, minutes: 30 },
  format: 'DD:HH:MM:SS' 
});
// Result: 02:05:30:00

// Format: HH:MM:SS:MS
const timedown4 = useTimedown({ 
  seconds: 60,
  format: 'HH:MM:SS:MS' 
});
// Result: 00:01:00:000

Custom End Time

const { time } = useTimedown({
  seconds: 60,
  endTime: 10, // Stop at 10 seconds instead of 0
  onComplete: () => {
    console.log('Reached 10 seconds!');
  },
});

Full Control Example

function AdvancedTimedown() {
  const { 
    time, 
    start, 
    pause, 
    resume, 
    reset,
    isRunning,
    isPaused,
    isCompleted
  } = useTimedown({
    minutes: 5,
  });

  return (
    <div>
      <h1>{time.formatted}</h1>
      
      <div>
        <p>Days: {time.days}</p>
        <p>Hours: {time.hours}</p>
        <p>Minutes: {time.minutes}</p>
        <p>Seconds: {time.seconds}</p>
        <p>Total Seconds: {time.totalSeconds}</p>
      </div>

      <div>
        {!isRunning && !isCompleted && (
          <button onClick={start}>Start</button>
        )}
        {isRunning && (
          <button onClick={pause}>Pause</button>
        )}
        {isPaused && (
          <button onClick={resume}>Resume</button>
        )}
        <button onClick={() => reset()}>Reset</button>
        <button onClick={() => reset(600)}>Reset to 10 minutes</button>
      </div>

      <div>
        <p>Status:</p>
        <p>Running: {isRunning ? 'Yes' : 'No'}</p>
        <p>Paused: {isPaused ? 'Yes' : 'No'}</p>
        <p>Completed: {isCompleted ? 'Yes' : 'No'}</p>
      </div>
    </div>
  );
}

Component Examples (Render Props Pattern)

Basic Component Usage

<Timedown seconds={60}>
  {({ time, start, pause, isRunning }) => (
    <div>
      <h1>{time.formatted}</h1>
      <button onClick={isRunning ? pause : start}>
        {isRunning ? 'Pause' : 'Start'}
      </button>
    </div>
  )}
</Timedown>

Custom Styled Component

<Timedown minutes={5} autoStart={false}>
  {({ time, start, pause, isRunning }) => (
    <div className="custom-timedown">
      <div className="timer-display">
        {time.minutes}:{time.seconds.toString().padStart(2, '0')}
      </div>
      <button onClick={isRunning ? pause : start}>
        {isRunning ? '⏸ Pause' : '▶ Start'}
      </button>
    </div>
  )}
</Timedown>

With Progress Bar

<Timedown seconds={60}>
  {({ time, start, pause, reset, isRunning }) => {
    const progress = (time.totalSeconds / 60) * 100;
    
    return (
      <div>
        <div className="progress-bar">
          <div 
            className="progress-fill" 
            style={{ width: `${progress}%` }} 
          />
        </div>
        <div className="time">{time.formatted}</div>
        <div className="controls">
          <button onClick={isRunning ? pause : start}>
            {isRunning ? 'Pause' : 'Start'}
          </button>
          <button onClick={() => reset()}>Reset</button>
        </div>
      </div>
    );
  }}
</Timedown>

Circular Progress Indicator

<Timedown seconds={60}>
  {({ time, start, isRunning }) => {
    const progress = (time.totalSeconds / 60) * 100;
    const circumference = 2 * Math.PI * 45;
    const offset = circumference - (progress / 100) * circumference;
    
    return (
      <div style={{ 
        position: 'relative', 
        width: '160px', 
        height: '200px',
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        gap: '16px'
      }}>
        <svg width="160" height="160">
          <circle
            cx="80"
            cy="80"
            r="70"
            stroke="#e5e7eb"
            strokeWidth="10"
            fill="none"
          />
          <circle
            cx="80"
            cy="80"
            r="70"
            stroke="url(#gradient)"
            strokeWidth="10"
            fill="none"
            strokeDasharray={circumference}
            strokeDashoffset={offset}
            strokeLinecap="round"
            transform="rotate(-90 80 80)"
          />
          <defs>
            <linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="100%">
              <stop offset="0%" style={{ stopColor: '#6366f1', stopOpacity: 1 }} />
              <stop offset="100%" style={{ stopColor: '#8b5cf6', stopOpacity: 1 }} />
            </linearGradient>
          </defs>
        </svg>
        <div style={{ 
          position: 'absolute', 
          top: '65px',
          display: 'flex',
          flexDirection: 'column',
          alignItems: 'center',
          gap: '4px'
        }}>
          <strong style={{ fontSize: '36px', fontWeight: 'bold', color: '#1f2937' }}>
            {time.seconds}
          </strong>
          <span style={{ fontSize: '12px', color: '#6b7280', textTransform: 'uppercase' }}>
            seconds
          </span>
        </div>
        <button 
          onClick={start}
          style={{
            padding: '8px 24px',
            borderRadius: '8px',
            border: 'none',
            background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
            color: 'white',
            fontWeight: '600',
            cursor: 'pointer',
            transition: 'transform 0.2s',
          }}
          onMouseEnter={(e) => e.currentTarget.style.transform = 'scale(1.05)'}
          onMouseLeave={(e) => e.currentTarget.style.transform = 'scale(1)'}
        >
          {isRunning ? 'Running...' : 'Start'}
        </button>
      </div>
    );
  }}
</Timedown>

API Reference

useTimedown(props) Hook

Props

Time Input (choose ONE approach):

| Prop | Type | Description | |------|------|-------------| | seconds | number | Simple: countdown from X seconds | | minutes | number | Simple: countdown from X minutes | | hours | number | Simple: countdown from X hours | | days | number | Simple: countdown from X days | | duration | Duration | Complex: combined time units | | until | Date | Countdown until a specific date/time |

Note: You can combine seconds, minutes, hours, and days - they will add up!

Options:

| Prop | Type | Default | Description | |------|------|---------|-------------| | autoStart | boolean | false | Whether to start countdown automatically | | format | TimeFormat | 'HH:MM:SS' | Time format (see formats below) | | customFormat | (time: FormattedTime) => string | undefined | Custom format function | | interval | number | 1000 | Update interval in milliseconds | | endTime | number | 0 | End countdown at specific second | | onComplete | () => void | undefined | Callback when countdown completes | | onTick | (time: FormattedTime) => void | undefined | Callback on each tick |

Return Value

{
  time: FormattedTime;      // Current time object
  start: () => void;         // Start the countdown
  pause: () => void;         // Pause the countdown
  resume: () => void;        // Resume the countdown
  reset: (newTime?: ResetTimeInput) => void; // Reset countdown
  isRunning: boolean;        // Whether countdown is running
  isPaused: boolean;         // Whether countdown is paused
  isCompleted: boolean;      // Whether countdown is completed
}

<Timedown> Component

The Timedown component uses the render props pattern, providing complete UI control while handling all countdown logic internally.

Props

Same as useTimedown props plus:

| Prop | Type | Description | |------|------|-------------| | children | (timedown: UseTimedownReturn) => ReactNode | Required - Render function that receives countdown state and methods |

Example

<Timedown seconds={60} autoStart>
  {({ time, start, pause, reset, isRunning, isPaused, isCompleted }) => (
    // Your custom UI here
  )}
</Timedown>

Types

Duration

interface Duration {
  seconds?: number;
  minutes?: number;
  hours?: number;
  days?: number;
}

ResetTimeInput

type ResetTimeInput = Duration | Date;

TimeFormat

type TimeFormat = 
  | 'HH:MM:SS'      // 01:30:45
  | 'MM:SS'         // 90:45
  | 'HH:MM:SS:MS'   // 01:30:45:123
  | 'DD:HH:MM:SS'   // 02:01:30:45
  | 'custom';       // Use customFormat function

FormattedTime

interface FormattedTime {
  days: number;
  hours: number;
  minutes: number;
  seconds: number;
  milliseconds: number;
  totalSeconds: number;
  formatted: string;
}

Utility Functions

You can also import utility functions for time conversion and formatting:

import { convertToSeconds, convertSecondsToUnits, formatTime } from 'react-timedown-ui';

// Convert various inputs to seconds
const seconds1 = convertToSeconds(60); // 60
const seconds2 = convertToSeconds({ minutes: 2, seconds: 30 }); // 150
const seconds3 = convertToSeconds(new Date('2025-12-31')); // seconds until date

// Convert seconds to time units
const units = convertSecondsToUnits(3665);
// { days: 0, hours: 1, minutes: 1, seconds: 5, milliseconds: 0, totalSeconds: 3665 }

// Format time
const formatted = formatTime(units, 'HH:MM:SS');
// "01:01:05"

Examples

Check out the Storybook examples for interactive demos and live code examples.

Contributing

Contributions are welcome! Please read our Contributing Guide for details on our code of conduct, development process, and how to submit pull requests.

License

MIT © React Timedown Contributors

Support


Made with ❤️ for the React community