Skip to content
WANCRAFT

G.119Data & Feedback

Countdown

A countdown to a date with rolling digits, progress rings, localised unit labels and a hydration-safe start.

NewReactMotionSVGTailwindintermediate

Overview

Countdown counts down to a Date, an ISO string or a timestamp in days, hours, minutes and seconds. Digits roll in as they change, an optional ring around each segment shows how much of that unit is left, and unit labels come from Intl, so locale="ms-MY" or "de-DE" just works. Every countdown on the page shares one second-aligned clock that stops in hidden tabs, and offscreen instances stop re-rendering. The server renders a placeholder and ticking starts after mount, so there is never a hydration mismatch. When it reaches zero it shows a completed state, announces it and calls onComplete once. Screen readers get role="timer" with a minute-level summary instead of a number that changes every second.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/countdown.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/countdown.json

Method 02 · Copy and paste

  1. 1. Install the dependencies below.
  2. 2. Copy the source from the Code tab into components/ui/countdown.tsx.
  3. 3. Import it and adjust props.
npm install motion
Dependencies
motion
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { Countdown } from "@/components/ui/countdown";

export function CountdownExample() {
  return (
    <Countdown
      target="2026-12-31T23:59:59Z"
      onComplete={() => console.log("Launched")}
    />
  );
}

03Props

PropTypeDefaultDescription
targetDate | string | number—Required. Date, ISO string or epoch milliseconds to count down to.
format"full" | "compact""full"Large labelled segments or one inline line.
size"sm" | "md" | "lg""md"Segment size in the full format.
showSecondsbooleantrueShow the seconds segment.
ringsbooleantrueProgress ring around each segment.
rollbooleantrueRoll digits as they change.
localestring"en-US"Locale for unit labels (Intl) and the summary.
dayRangenumber30Days that make a full days ring.
completedTextstring"Time’s up"Shown and announced on completion.
labelstring"Countdown"Accessible name of the timer.
accentstring"#FF7448"Ring and completed-state colour.
onComplete() => void—Called once after mount when the time reaches zero (also when the target is already past).

04Features

  • 01Accepts Date, ISO string or epoch milliseconds
  • 02Rolling digits and per-unit progress rings
  • 03Full and compact formats, with or without seconds
  • 04Intl unit labels for any locale
  • 05One shared, second-aligned clock that stops in hidden tabs; offscreen instances stop re-rendering
  • 06Server renders a placeholder and ticking starts after mount — no hydration mismatch
motion · tweeninput · viewport

05Accessibility

  • role="timer" with aria-live="off": the per-second digits are aria-hidden and never announced.
  • A visually hidden summary such as “2 days, 6 hours and 41 minutes remaining” changes at most once per minute.
  • Completion is announced once through a polite status region.
  • With prefers-reduced-motion digits change without rolling and rings without sweeping.

06Limitations

  • Days beyond 99 widen the days segment; very long countdowns read better in the compact format.