react-timedown-ui
v1.1.0
Published
A headless, flexible timedown timer hook and component for React applications
Maintainers
Readme
⏰ React Timedown
A flexible, headless countdown timer hook and component for React applications. Built with TypeScript and designed for maximum flexibility.
📚 Documentation
- Interactive Examples (Storybook) - Try live examples
- Documentation Site - Complete guide
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-uior
yarn add react-timedown-uiQuick 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 secondsUsing 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:000Custom 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 functionFormattedTime
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
