Skip to content
Devix Open Source

Guide

Getting started

npm install @devix-labs/countdown
import { createCountdownWidget } from '@devix-labs/countdown';
import '@devix-labs/countdown/styles.css';

createCountdownWidget(document.querySelector('#sale'), {
  deadline: '2026-12-31T23:59:59Z',
});
<dx-countdown deadline="2026-12-31T23:59:59Z" variant="clock"></dx-countdown>

What it sounds like

This is the part worth reading, because it is what everything else here gets wrong.

A countdown wired into an aria-live region announces the time every second. On a page with a screen reader that is not a feature — it makes everything else unusable, because the region interrupts continuously.

So the digits are aria-hidden inside a <time datetime="…">, and a separate polite region speaks only when it is worth speaking: when the leading unit changes — days to hours, hours to minutes — and at an hour, ten minutes, five, one, thirty seconds, ten, and the last five.

Counting two minutes down to zero, it speaks fewer than fifteen times instead of a hundred and twenty.

createCountdownWidget(el, {
  deadline,
  announceAt: [60, 10, 3, 2, 1],   // your own moments, in seconds
});

Three shapes

createCountdownWidget(el, { deadline, variant: 'boxes' });   // a number per unit
createCountdownWidget(el, { deadline, variant: 'clock' });   // 01:23:45
createCountdownWidget(el, { deadline, variant: 'words' });   // "2 days, 3 hours"

Leading units that are zero are dropped, so forty-five seconds is one box rather than 00:00:00:45. Pass trim: false if you want them all.

It is right when the tab wakes up

Every value is worked out from deadline − now(), never counted up from ticks. A counter that adds one per tick is wrong by however long the tab slept and has no way to find out; this simply asks the clock.

const timer = createCountdownWidget(el, { deadline });
timer.read();   // { days, hours, minutes, seconds, total, done }

Following the server's clock, not the visitor's

A sale ending "in ten minutes" by your server has already ended on a machine whose clock is twenty minutes fast — and some are set that way deliberately.

import { serverClock, createCountdownWidget } from '@devix-labs/countdown';

const response = await fetch('/api/time');
const now = serverClock(Date.parse(response.headers.get('date')));

createCountdownWidget(el, { deadline, now });

serverClock works out the offset once and applies it from then on, so there is no request per tick.

Any language

Everything goes through Intl:

createCountdownWidget(el, { deadline, locale: 'ar-EG', variant: 'clock' });
// ٠٢:٠٣:٠٤:٠٥

Arabic gets the dual — يومان for two days, which is a grammatical number English does not have — and Russian picks correctly among its three plural forms. No table of English words anywhere.

Where to go next

Updated 15 Sep 2026