# Countdown

> 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.

- Type: React component (Data Display)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add countdown`
- Docs: https://wingo-ui.com/components/countdown

## Interaction

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.

## Installation

```bash
npx wingo-ui@latest add countdown
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/countdown` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/ui/countdown.tsx`

npm dependencies: `npm i motion`

Wingo UI dependencies (installed automatically):

- [Animated Counter](https://wingo-ui.com/components/animated-counter) ([Markdown](https://wingo-ui.com/components/animated-counter.md))
- [Relative time](https://wingo-ui.com/components/relative-time) ([Markdown](https://wingo-ui.com/components/relative-time.md))
- [useLocale](https://wingo-ui.com/components/use-locale) ([Markdown](https://wingo-ui.com/components/use-locale.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Number format](https://wingo-ui.com/components/number-format) ([Markdown](https://wingo-ui.com/components/number-format.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
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

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `to` | `Date \| number \| string` |  | The 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. |
| `now?` | `Date \| number` |  | Pins 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. |
| `units?` | `CountdownUnit[] \| "auto"` | `"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). |
| `variant?` | `"blocks" \| "inline" \| "text"` | `"blocks"` | 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. |
| `size?` | `"sm" \| "md" \| "lg"` | `"md"` | 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. |
| `animation?` | `"roll" \| "fade" \| "none"` | `"roll"` | roll: odometer wheels from AnimatedCounter. fade: each changed digit crossfades in place. none: plain text. Reduced motion always behaves as none. |
| `tileVariant?` | `"soft" \| "solid" \| "outline" \| "elevated"` | `"soft"` | The look of the tiles in blocks: a tint, a solid fill, a hairline outline, or a raised card surface. |
| `tone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | 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. |
| `color?` | `string` | `""` | Any CSS color. Replaces the tone; text on a solid tile turns black or white by lightness. |
| `radius?` | `"none" \| "sm" \| "md" \| "lg" \| "full"` | `"md"` | Corner shape of the tiles and the done box, scaled with the size (md is 12 / 16 / 24px). |
| `showLabels?` | `boolean` | `true` | Unit names under the blocks; in inline the letter after the days (2d). Without labels, blocks show the separator between tiles. |
| `showSeconds?` | `boolean` | `true` | false hides seconds and ticks once a minute: calmer, and cheaper on long lists of cards. |
| `padStart?` | `boolean` | `true` | 04 instead of 4 for hours, minutes and seconds. Days are never padded and grow past 99 without a cap. |
| `separator?` | `string` | `":"` | Between 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. |
| `precision?` | `number` | `2` | text variant: how many units the phrase names, largest first ("in 3 days and 4 hours"). |
| `align?` | `"start" \| "center" \| "end"` | `"center"` | blocks: where the row sits in its container (the blocks root is full width). |
| `completedContent?` | `ReactNode` | `""` | Replaces 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". |
| `locale?` | `string` | `""` | Locale for the digits and the plural rules your labels use. Empty follows UIProvider, then the page. |
| `onComplete?` | `() => void` |  | Fires 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. |
| `interval?` | `number` | `0` | ms 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. |
| `labels?` | `Partial<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". |
| `classNames?` | `Partial<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. |
| `className?` | `string` |  | Extra classes for the root, merged last. The root carries data-state (idle, running, done) and data-urgent in the last minute. |

## Parts

### useCountdown (hook)

The 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 (function)

The 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 (constant)

The 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.

## See also

- [Animated Counter](https://wingo-ui.com/components/animated-counter) ([Markdown](https://wingo-ui.com/components/animated-counter.md))
- [Progress](https://wingo-ui.com/components/progress) ([Markdown](https://wingo-ui.com/components/progress.md))
- [Relative time](https://wingo-ui.com/components/relative-time) ([Markdown](https://wingo-ui.com/components/relative-time.md))

---

Canonical: https://wingo-ui.com/components/countdown
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
