React Integration Guide
@timekeeper-countdown/react exposes the useCountdown hook. This page gathers the most common patterns in one place so you can wire the hook into your components with confidence.
Installation
npm install @timekeeper-countdown/reactThe hook lists react and react-dom as peer dependencies (React 17+). The shared engine and helper utilities are bundled automatically.
Basic Hook Usage
import { useCountdown } from '@timekeeper-countdown/react';
import { formatTime } from '@timekeeper-countdown/core/format';
export function CountdownCard() {
const countdown = useCountdown(90, {
autoStart: false,
});
const { minutes, seconds } = formatTime(countdown.snapshot);
return (
<article>
<h2>
{minutes}:{seconds}
</h2>
<p>Status: {countdown.state}</p>
<button onClick={countdown.start} disabled={countdown.isRunning}>
Start
</button>
<button onClick={countdown.pause} disabled={!countdown.isRunning}>
Pause
</button>
<button onClick={() => countdown.reset(90)}>Reset</button>
</article>
);
}Options Recap
const result = useCountdown(initialSeconds, {
autoStart,
tickIntervalMs,
timeProvider,
onSnapshot,
onStateChange,
onError,
});autoStart?: boolean– start immediately on mount (defaultfalse).tickIntervalMs?: number– polling interval in milliseconds (default100).timeProvider?: TimeProvider | (() => number)– swap the clock source; pass adapters from@timekeeper-countdown/core/testing-utilsfor deterministic control.onSnapshot?: (snapshot) => void– run side effects on every update.onStateChange?: (state, snapshot) => void– observe state transitions.onError?: (error) => void– capture unexpected engine issues.
All options are optional; the defaults cover most scenarios.
Returned Fields
useCountdown returns the current snapshot plus memoised control methods. Every render receives a fresh snapshot, but the control functions remain referentially stable.
const { snapshot, state, totalSeconds, parts, isRunning, isCompleted, start, pause, resume, reset, stop, setSeconds } =
useCountdown(300);snapshotincludesinitialSeconds,totalSeconds, derivedparts, and helper flags.partsbreaks down time into numbers (minutes,hours, etc.).isRunning/isCompletedare convenience booleans.start/pause/resume/reset/stopreturnfalsewhen the transition is invalid for the current state.setSecondsadjusts the remaining time without changing the current state, ideal for "skip" or "extend" buttons.
Custom Time Providers
import { createFakeTimeProvider, toTimeProvider } from '@timekeeper-countdown/core/testing-utils';
const fake = createFakeTimeProvider({ startMs: 0 });
function ControlledCountdown() {
const countdown = useCountdown(30, {
autoStart: true,
timeProvider: toTimeProvider(fake),
tickIntervalMs: 10,
});
return (
<div>
<p>{countdown.totalSeconds}s</p>
<button onClick={() => fake.advance(1000)}>Advance 1 second</button>
</div>
);
}Passing a fake provider keeps your tests synchronous and deterministic. You can also use this hook to sync multiple countdowns to a shared clock.
Multiple Timers
import { useEffect } from 'react';
import { useCountdown } from '@timekeeper-countdown/react';
function MultiTimerDashboard() {
const focus = useCountdown(1500);
const breakTimer = useCountdown(300);
const { isCompleted: focusCompleted } = focus;
const { reset: resetBreak, start: startBreak } = breakTimer;
useEffect(() => {
if (focusCompleted) {
resetBreak();
startBreak();
}
}, [focusCompleted, resetBreak, startBreak]);
return (
<div>
<TimerCard title="Focus" countdown={focus} />
<TimerCard title="Break" countdown={breakTimer} />
</div>
);
}Each hook call is isolated; just pass the result object into child components that know how to render a countdown.
Cleanup
No extra cleanup is required. The hook destroys the underlying engine when the component unmounts. If you ever instantiate the engine manually (not recommended for this release), call destroy() yourself.
Looking Ahead
Adapters for Angular, Vue, Svelte, and vanilla JavaScript are in development. They will expose the same state machine, snapshot shape, and helper utilities described here, so anything you learn while building React components will transfer smoothly.