Skip to main content
Copy for LLM

Countdown

<x-wirekit::countdown> shows the time remaining until an absolute deadline, ticking down every second on the client — no wire:poll, no server round-trips. It colors itself when the deadline is close (urgent) and again once it has passed (overdue), so a running deadline reads at a glance instead of forcing the reader to subtract a timestamp from the current time in their head.

Pass an absolute instant (a Carbon, an ISO-8601 string, or a unix timestamp) — never a duration. A duration drifts the moment the tab sleeps or the page is cached; an absolute instant is recomputed from the real clock on every tick.

Usage

The largest meaningful unit leads and shrinks down to the smallest — years appear only once the remaining time reaches a year, so a far-off deadline reads in years and days instead of tens of thousands of days. A year counts as 365 days, so a deadline years away shows one day more for each leap day in between; leave years out of units when the count has to match the calendar:

Countdown to a deadline

Urgent threshold

Set warn-threshold (in seconds) to switch the countdown to its urgent color before the deadline slips — here, the last hour:

Urgent in the final hour

Overdue

Once the deadline has passed, the countdown shows its overdue text in the danger color. Provide your own copy with expired-text:

Overdue deadline

Without seconds

For a long deadline, drop the churning seconds with :show-seconds="false":

Days and hours only

Choose your units

Pass an explicit ordered subset to units — any combination of years, days, hours, minutes, seconds. The largest unit you list carries all the overflow above it (so units="hours minutes" shows the total hours), and the smallest truncates. Large values get locale-aware thousands separators (like price); pass locale to override, or :separators="false" to turn them off:

Days, hours, minutes only

Segments variant

variant="segments" renders each unit as its own boxed block with a label — the classic dashboard countdown look. When a value changes, it animates (the default box pulse — see Change animation for the styles):

Boxed segments

Change animation

The segments variant animates each unit as its value changes, in one of two styles set with animate. It plays for the first seconds after the countdown appears, most visibly on the seconds box, and then stops while the value keeps ticking, so the countdown never blinks for longer than the five seconds WCAG 2.2.2 allows without a control to stop it. Both styles honor prefers-reduced-motion (an instant, motionless update for users who ask for less motion).

animate="box" (the default) pulses the whole box — border and background flash the accent color with a small scale pop:

Box pulse — the whole box flashes

animate="text" flashes only the changing number's color (to your theme's accent) — the box stays still. It is the understated alternative to the box pulse; it reads strongest on a theme with a colored accent:

Text flash — only the number changes color

Turn motion off entirely with :animate="false":

Motion off

Livewire

The countdown ticks entirely on the client, so it never triggers a Livewire round-trip. Bind the deadline itself as a normal prop:

<x-wirekit::countdown :until="$order->payment_due_at" :warn-threshold="600" />

Completion events. The countdown is more than display — it notifies when the deadline passes, so a sibling control can react (re-enable a button, refetch, show a retry form):

  • wirekit-countdown-expired — a bubbling event dispatched once the moment the deadline is reached (or immediately for an already-past deadline).
  • x-modelable="done" — a writable boolean that flips to true at expiry, so you can observe completion with x-model.
{{-- Re-enable "Resend code" only after the retry window elapses --}}
<div x-data="{ ready: false }">
    <x-wirekit::countdown :until="now()->addSeconds($retryAfter)" x-model="ready" />
    <x-wirekit::button x-bind:disabled="!ready" wire:click="resend">Resend code</x-wirekit::button>
</div>

The retry-timer pattern pairs naturally with a rate limiter — feed it RateLimiter::availableIn() via :until="now()->addSeconds($retryAfter)".

Headless: your own copy around the number

Sometimes you want the countdown's clock — the client tick, the deadline recompute on wake, the completion event — but your own wording around the number ("Resend available in 45 seconds", with your locale's pluralization). Pass a slot and the countdown becomes headless: it renders your markup instead of the default units, and your Alpine expressions resolve against its live state.

<x-wirekit::countdown :until="now()->addSeconds($retryAfter)">
    {{-- `remaining` is the full breakdown plus totals; `expired` / `urgent` /
         `done` are the same states the default display uses. --}}
    <span x-show="!expired" x-text="`Resend available in ${remaining.totalSeconds} seconds`"></span>
    <span x-show="expired">You can resend now.</span>
</x-wirekit::countdown>

The reactive state available inside the slot:

Expression What it gives you
remaining { years, days, hours, minutes, seconds, totalSeconds, totalMs } — the complete canonical ladder plus totals, independent of the units / variant props.
expired true once the deadline has passed.
urgent true inside the warn-threshold window.
done The writable completion boolean (also x-modelable).

In headless mode you own the accessible text too — the default screen-reader <time> is not rendered, so add your own (for example a one-time role="status" announcement at zero) to match your app's pattern.

Props

Prop Type Default Description
until DateTimeInterface|string|int null The absolute target instant: a date object of any class (Carbon, CarbonImmutable, DateTime), an ISO-8601 string or a Unix timestamp. A date object counts to the instant it holds in its own zone, so a model attribute counts the same whether your application keeps the default dates or has called Date::use(CarbonImmutable::class). A null value renders as immediately overdue.
expiredText string|null null Text shown once the deadline has passed. Defaults to a translatable "Overdue".
warnThreshold int|null null Seconds-remaining threshold below which the countdown turns urgent (warning color). Omit for no urgent state.
units string 'auto' 'auto' shows years→seconds and drops leading zero-units, or an explicit ordered subset like "days hours minutes". The largest listed unit carries the overflow. A year counts as 365 days.
showSeconds bool true Include seconds in 'auto' mode (ignored when units is an explicit list).
variant string 'inline' 'inline' (e.g. 73y 190d 12h) or 'segments' (boxed blocks with labels).
separators bool true Locale-aware thousands separators on large unit values.
locale string|null null BCP-47 locale for the separators. Defaults to the app locale.
timezone string|null null The zone the deadline is written in for the reader, such as Europe/Berlin: the deadline in the accessible name and the machine-readable time. Defaults to the zone of the value. The countdown counts to the same instant either way.
animate bool|string true Change animation. For segments: "box" (default — the whole box pulses with a border + accent flash + scale pop) or "text" (only the changing number flashes color). false / "none" turns it off. It plays for the first seconds after the countdown appears, then stops. The inline variant changes its value without animation. Honors prefers-reduced-motion.
scope string|null null Scoped personalization key.

Accessibility

  • The root carries role="timer", which is a live region set to off — so the ticking value is not announced every second.
  • The accessible name is the absolute deadline (aria-label), a stable value a screen reader reads on navigation.
  • A visually hidden <time datetime> holds a coarse "time remaining" phrase in the active units (e.g. "2 days, 5 hours"), so assistive tech can read the current remaining time on demand without per-second spam.
  • The visible ticking units are aria-hidden — a decorative convenience over the semantic time.
  • The change animation of the segments variant stops a few seconds after the countdown appears. The value itself keeps updating: where the countdown stands beside other content and the seconds are not essential to the task, a unit list without seconds changes once a minute instead of every second.

Keyboard Interaction

This component is purely presentational and does not respond to keyboard input.

Updated in WireKit v2.66.0 (2026-10-13)

Was this page helpful?

Thank you for your feedback!

Voting requires cookies or local storage. What we store