Countdown React component for Tailwind CSS v4
v1.0.0Added in 1.0.0
A React countdown timer to a launch, sale or deadline, shown as rolling digit blocks, inline in a sentence or in words, with done content.
Nothing to press: it counts down on its own and each changed digit rolls into place on a spring (or crossfades, or simply swaps with reduced motion). Leading zero units drop out as time passes, so a launch two days away shows days while the last hour shows only minutes and seconds. At zero onComplete fires once, the done content springs in and screen readers hear the finish once; the ticking itself is never announced, a sentence updated once a minute is read instead. A tab that slept jumps straight to the right value. In the playground pick when the launch is (in 10 s, in 2 days, in 45 days or already past) to watch it tick, finish, or render done.
Countdown is a Pro React component in the React data display components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add countdown, together with Animated Counter, Relative time, useLocale, Motion presets, Number format and 2 more and the npm package motion.
Add to a project
Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add countdownCountdown is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans
Usage
Import Countdown from @/components/ui/countdown.
import { Countdown } from "@/components/ui/countdown"
import { toast } from "@/components/ui/toast"
export function LaunchCountdown() {
return (
<Countdown
to="2026-09-26T20:52:09+03:00"
size="lg"
completedContent="Acme CRM 4.0 is live"
onComplete={() => toast.success("The launch has started")}
// labels: plural functions per unit, e.g. days: (count, formatted) => `${formatted} ${count === 1 ? "day" : "days"}`
/>
)
}Props
to*Date | number | stringThe moment to count down to: a Date, ms since epoch or an ISO string. An invalid value renders the done state and logs nothing. Changing it recomputes at once and re-arms onComplete. The playground sets it through the buttons under the card.
nowDate | numberPins the clock (demos, screenshots, server rendering). Left out, the live clock ticks exactly on the unit boundaries of `to` (and re-syncs when the tab wakes); until it is known after hydration the digits render as -- at their final width.
unitsdefault"auto"CountdownUnit[] | "auto"auto shows days, hours, minutes and seconds and drops leading zero units (never "0 days"), always keeping the last two. An explicit list like ["hours", "minutes"] folds the rest into the largest unit (52 hours).
variantdefault"blocks""blocks" | "inline" | "text"blocks: a tile per unit with its label under it. inline: 2d 04:12:09 in the flow of a sentence. text: words, "in 2 days and 4 hours", built from labels.
sizedefault"md""sm" | "md" | "lg"blocks: 40 / 56 / 80px tiles with 20 / 28 / 44px numerals (lg shrinks to 36px numerals below 400px so four units fit a 360px phone). inline 13 / 15 / 20px, text 13 / 14 / 16px.
animationdefault"roll""roll" | "fade" | "none"roll: odometer wheels from AnimatedCounter. fade: each changed digit crossfades in place. none: plain text. Reduced motion always behaves as none.
tileVariantdefault"soft""soft" | "solid" | "outline" | "elevated"The look of the tiles in blocks: a tint, a solid fill, a hairline outline, or a raised card surface.
tonedefault"neutral""neutral" | "brand" | "success" | "warning" | "danger" | "info"Semantic color of the tiles and digits. neutral is a quiet foreground tint. It never changes on its own near the end; style [data-urgent] if you want that.
colordefault""stringAny CSS color. Replaces the tone; text on a solid tile turns black or white by lightness.
radiusdefault"md""none" | "sm" | "md" | "lg" | "full"Corner shape of the tiles and the done box, scaled with the size (md is 12 / 16 / 24px).
showLabelsdefaulttruebooleanUnit names under the blocks; in inline the letter after the days (2d). Without labels, blocks show the separator between tiles.
showSecondsdefaulttruebooleanfalse hides seconds and ticks once a minute: calmer, and cheaper on long lists of cards.
padStartdefaulttrueboolean04 instead of 4 for hours, minutes and seconds. Days are never padded and grow past 99 without a cap.
separatordefault":"stringBetween units in inline, and between tiles in blocks when showLabels is off. An empty string in inline gives every unit its letter: 2d 4h 12m 09s.
precisiondefault2numbertext variant: how many units the phrase names, largest first ("in 3 days and 4 hours").
aligndefault"center""start" | "center" | "end"blocks: where the row sits in its container (the blocks root is full width).
completedContentdefault""ReactNodeReplaces the digits when time is up, like "The offer has ended", springing in on a bouncy scale. Without it the digits stay at zero. Try it with "in 10 s".
localedefault""stringLocale for the digits and the plural rules your labels use. Empty follows UIProvider, then the page.
onComplete() => voidFires once on the transition to zero while mounted, on time even when seconds are hidden. A to already past at mount renders done without calling it.
intervaldefault0numberms between ticks (at least 1000), aligned to the target so the last tick lands exactly at zero. Unset (0) ticks every second, or every minute with showSeconds off.
labelsPartial<CountdownLabels>Every string, English by default (DEFAULT_COUNTDOWN_LABELS): days / hours / minutes / seconds (count, formatted) => "3 days", names { days: (count) => "days" } under the blocks, short { days: "d", ... }, sentence(text, parts) => "3 days and 4 hours left", done "Time's up", and "and", in "in".
classNamesPartial<Record<"units" | "unit" | "value" | "label" | "separator" | "done", string>>Extra classes for the inner parts: the row, each unit, the tile or digits, the label, the separator, the done content.
classNamestringExtra classes for the root, merged last. The root carries data-state (idle, running, done) and data-urgent in the last minute.
Parts
useCountdown
hookThe clock behind the component for custom layouts: { days, hours, minutes, seconds, totalMs, done }, recomputed from the absolute time on every tick and on waking, with ticks aligned to the target. totalMs (rounded up to the interval) is null until the clock is known. Signature: (to: Date | number | string, options?: { now?; interval? = 1000; onComplete?; units? }) => CountdownParts.
getCountdownParts
functionThe pure split of the time left into units: the largest unit carries the overflow, the smallest rounds up. Signature: (to, now, units?) => CountdownParts.
DEFAULT_COUNTDOWN_LABELS
constantThe English strings, to spread under a translation. Type: CountdownLabels.
Examples
Inline in a listing card
variant="inline" size="sm" sits in a sentence and rolls in place.
Launch hero
size="lg" with the brand tone. Four units still fit a 360px phone.
Trial notice in words
variant="text" with seconds hidden: ticks once a minute.
Completed content
Ten seconds on the real clock, then the done content springs in and onComplete raises a toast.
Romanian plurals
Labels as functions for a language with three plural forms: Romanian, with its own locale.
useCountdown for a custom layout
The hook drives a progress ring for a 60 s OTP resend.
Tile styles
soft, solid (with a custom color), outline and elevated; separators when labels are off.
Files
- components/ui/countdown.tsx
FAQ
Is the React Countdown component free?
The preview, props and docs are free; copying the source needs a Pro plan. See plans.
How do I install Countdown?
Run npx wingo-ui@latest add countdown. It also adds the Wingo UI items it builds on (Animated Counter, Relative time, useLocale, Motion presets, Number format and 2 more) and the npm package motion. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/countdown.
See also
Used in
More React data display components
Articles using this component
- React Countdown Timer: Blocks, Inline, Sleeping Tabs
Component guides9 min read
- Nextjs Waitlist Page: Countdown, Referrals, Real Counts
Build it: screens and apps10 min read