
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
react-timedown-ui
Advanced tools
A headless, flexible timedown timer hook and component for React applications
A flexible, headless countdown timer hook and component for React applications. Built with TypeScript and designed for maximum flexibility.
npm install react-timedown-ui
or
yarn add react-timedown-ui
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>
);
}
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>
);
}
// Most straightforward - just pass seconds
const { time, start } = useTimedown({
seconds: 60, // Clear: 60 seconds
autoStart: false,
});
// Or use minutes prop
const { time, pause } = useTimedown({
minutes: 5, // Clear: 5 minutes
autoStart: true,
});
// Or hours
const { time } = useTimedown({
hours: 2, // Clear: 2 hours
autoStart: true,
});
// Combine multiple units - they add up!
const { time } = useTimedown({
hours: 1,
minutes: 30,
seconds: 45,
autoStart: true,
});
// Total: 5445 seconds
// For complex durations, use the duration prop
const { time } = useTimedown({
duration: {
days: 2,
hours: 5,
minutes: 30,
seconds: 15,
},
autoStart: true,
});
const newYear = new Date('2026-01-01T00:00:00');
const { time } = useTimedown({
until: newYear, // Countdown until New Year
autoStart: true,
});
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.
},
});
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`;
},
});
// 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
const { time } = useTimedown({
seconds: 60,
endTime: 10, // Stop at 10 seconds instead of 0
onComplete: () => {
console.log('Reached 10 seconds!');
},
});
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>
);
}
<Timedown seconds={60}>
{({ time, start, pause, isRunning }) => (
<div>
<h1>{time.formatted}</h1>
<button onClick={isRunning ? pause : start}>
{isRunning ? 'Pause' : 'Start'}
</button>
</div>
)}
</Timedown>
<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>
<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>
<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>
useTimedown(props) HookTime 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 |
{
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> ComponentThe Timedown component uses the render props pattern, providing complete UI control while handling all countdown logic internally.
Same as useTimedown props plus:
| Prop | Type | Description |
|---|---|---|
children | (timedown: UseTimedownReturn) => ReactNode | Required - Render function that receives countdown state and methods |
<Timedown seconds={60} autoStart>
{({ time, start, pause, reset, isRunning, isPaused, isCompleted }) => (
// Your custom UI here
)}
</Timedown>
Durationinterface Duration {
seconds?: number;
minutes?: number;
hours?: number;
days?: number;
}
ResetTimeInputtype ResetTimeInput = Duration | Date;
TimeFormattype 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
FormattedTimeinterface FormattedTime {
days: number;
hours: number;
minutes: number;
seconds: number;
milliseconds: number;
totalSeconds: number;
formatted: string;
}
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"
Check out the Storybook examples for interactive demos and live code examples.
Contributions are welcome! Please read our Contributing Guide for details on our code of conduct, development process, and how to submit pull requests.
MIT © React Timedown Contributors
Made with ❤️ for the React community
FAQs
A headless, flexible timedown timer hook and component for React applications
We found that react-timedown-ui demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.