Skip to main content

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.