Core use cases
import { useTimer } from '@crup/react-timer-hook';
The root hook is the smallest path. It gives you lifecycle state, now, elapsedMilliseconds, and controls.
Wall clock
const clock = useTimer({ autoStart: true, updateIntervalMs: 1000 });
const date = new Date(clock.now);
Use this for live clocks, dashboard timestamps, and "last updated" UI. Your app chooses locale and formatting.
Stopwatch
const stopwatch = useTimer({ updateIntervalMs: 100 });
return (
<>
<output>{(stopwatch.elapsedMilliseconds / 1000).toFixed(1)}s</output>
<button disabled={!stopwatch.isIdle} onClick={stopwatch.start}>Start</button>
<button disabled={!stopwatch.isRunning} onClick={stopwatch.pause}>Pause</button>
<button disabled={!stopwatch.isPaused} onClick={stopwatch.resume}>Resume</button>
<button onClick={stopwatch.restart}>Restart</button>
</>
);
Use this for workouts, task timers, calls, recordings, and admin tools.
Absolute deadline
const timer = useTimer({
autoStart: true,
endWhen: snapshot => snapshot.now >= expiresAt,
onEnd: () => closeAuction(auctionId),
});
const remainingMs = Math.max(0, expiresAt - timer.now);
Use now for server deadlines: auctions, reservations, job expiry, and signed URL expiry.
Pausable duration countdown
const durationMs = 5 * 60 * 1000;
const timer = useTimer({
autoStart: true,
endWhen: snapshot => snapshot.elapsedMilliseconds >= durationMs,
});
const remainingMs = Math.max(0, durationMs - timer.elapsedMilliseconds);
Use elapsedMilliseconds for active-time countdowns where pause/resume should freeze progress.
OTP resend cooldown
const cooldownMs = 15_000;
const timer = useTimer({
autoStart: true,
updateIntervalMs: 250,
endWhen: snapshot => snapshot.elapsedMilliseconds >= cooldownMs,
});
const canResend = timer.isEnded || timer.isIdle;
Use this for OTP resend, email invite resend, rate-limited retry buttons, and "try again" cooldowns.
Error handling
onEnd runs once per generation. If the callback can fail, pass onError.
const timer = useTimer({
autoStart: true,
endWhen: snapshot => snapshot.elapsedMilliseconds >= 1000,
onEnd: () => saveCompletion(),
onError: error => reportError(error),
});
For live demos, see wall clock, stopwatch, absolute countdown, and OTP resend cooldown.