Skip to main content

useTimer

function useTimer(options?: UseTimerOptions): TimerSnapshot & TimerControls;

Options

type UseTimerOptions = {
autoStart?: boolean;
updateIntervalMs?: number;
endWhen?: (snapshot: TimerSnapshot) => boolean;
onEnd?: (snapshot: TimerSnapshot, controls: TimerControls) => void | Promise<void>;
onError?: (error: unknown, snapshot: TimerSnapshot, controls: TimerControls) => void;
};

updateIntervalMs defaults to 1000 and must be positive and finite.

endWhen ends the lifecycle when it returns true.

onEnd fires once per generation. restart() creates a new generation.

onError receives sync throws and async rejections from onEnd.

The root useTimer() export is lifecycle-only to keep the default bundle small. Use @crup/react-timer-hook/schedules when you need polling schedules and schedule timing context.

Controls

type TimerControls = {
start(): void;
pause(): void;
resume(): void;
reset(options?: { autoStart?: boolean }): void;
restart(): void;
cancel(reason?: string): void;
};

cancel(reason) is a terminal early stop and does not call onEnd.

Snapshot

type TimerSnapshot = {
status: 'idle' | 'running' | 'paused' | 'ended' | 'cancelled';
now: number;
tick: number;
startedAt: number | null;
pausedAt: number | null;
endedAt: number | null;
cancelledAt: number | null;
cancelReason: string | null;
elapsedMilliseconds: number;
isIdle: boolean;
isRunning: boolean;
isPaused: boolean;
isEnded: boolean;
isCancelled: boolean;
};

Use now for wall-clock math. Use elapsedMilliseconds for active elapsed duration.