Reference
Options
createCountdownWidget(element, {
deadline: '2026-12-31T23:59:59Z',
now: () => Date.now(),
variant: 'boxes',
precision: 0,
locale: undefined,
pad: true,
trim: true,
units: ['days', 'hours', 'minutes', 'seconds'],
daysInHours: false,
overtime: false,
autoStart: true,
showLabels: true,
doneText: undefined,
announceAt: [3600, 600, 300, 60, 30, 10, 5, 4, 3, 2, 1],
});
| Option | Default | |
|---|---|---|
deadline |
— | A Date, an ISO string, or milliseconds since the epoch. |
now |
the clock | Swap in serverClock() to follow your server. |
variant |
'boxes' |
'boxes', 'clock' or 'words'. |
precision |
0 |
Decimal places of a second. 2 shows hundredths. |
locale |
the browser's | Anything Intl accepts. |
pad |
true |
09 rather than 9. |
trim |
true |
Drop leading units that are zero. |
units |
all four | Which to show at all. |
daysInHours |
false |
36:00:00 rather than 1 day 12:00:00. |
overtime |
false |
Keep counting upwards past the deadline. |
autoStart |
true |
|
showLabels |
true |
Names under the numbers, for boxes. |
doneText |
"Time is up" | |
announceAt |
see above | Seconds at which a screen reader is told. |
theme, classNames, strings |
As in every Devix widget. |
Methods
const timer = createCountdownWidget(el, { deadline });
timer.read(); // { days, hours, minutes, seconds, milliseconds, total, done }
timer.setDeadline(newDate); // move it without rebuilding
timer.countdown.stop();
timer.countdown.start();
timer.destroy();
timer.countdown is the engine underneath, which you can also use on its own:
import { createCountdown } from '@devix-labs/countdown';
const countdown = createCountdown({
deadline,
onTick: (parts) => console.log(parts.total),
onComplete: () => refresh(),
});
onComplete fires exactly once, however the deadline is passed — including
by a clock that jumped an hour while the laptop was shut.
Ticking only when the display changes
There is no thousand-millisecond interval. Each tick schedules the next to land just after the moment the number actually changes:
import { untilNextChange } from '@devix-labs/countdown';
untilNextChange(1500, 0); // 504 — half a second to the next whole one
untilNextChange(1001, 0); // 5 — a millisecond past it
untilNextChange(1500, 2); // 14 — hundredths wake far more often
A countdown showing days does not need to wake every second, and one showing hundredths needs to wake far more often than that. Scheduling to the boundary is both cheaper and exact.
While the tab is hidden nothing is scheduled at all, and on return the value is recomputed at once rather than caught up one tick at a time.
The pieces, without a widget
import { partsOf, clock, words, relative, largestUnit } from '@devix-labs/countdown';
const parts = partsOf(deadline - Date.now());
clock(parts); // '02:03:04:05'
clock(parts, { locale: 'ar-EG' }); // '٠٢:٠٣:٠٤:٠٥'
words(parts, { locale: 'ru' }); // '2 дня, 3 часа…'
relative(parts); // 'in 3 days'
largestUnit(parts); // 'days'
All of it is pure, so it renders on a server too — though remember that a countdown rendered on a server is stale the moment it is sent, which is why the widget recomputes on mount.