Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Text

Pro

Countdown Text for React

A live countdown to a moment, its digits dropping into place as each second runs out. For React with Base UI, when a launch, a sale or a wait has a real deadline.

Framework

Countdown Text counts down to a moment, live: as each second runs out, the digits that change drop into their windows from above while the old ones fall away, so only what changed moves. It writes the time left with its units (3d 04h 05m 09s), in short or long words, or as a digital readout, and stops at zero, where it calls onComplete once.

Use it for a launch page, the end of a sale, an event lobby, or a wait before something can be done again. For the time of day, use Clock Text; for "in 5 minutes" or "5 minutes ago", Relative Time Text. A small timer wakes as each whole second runs out and the digits move with CSS keyframe animations the install adds to your stylesheet, with no animation library; with reduced motion the digits change in place.

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Usage

import { CountdownText } from "@/components/ui/countdown-text";
<CountdownText target="2027-01-01T00:00:00Z" />

Demos

Format

format is how the time left is written: narrow, the default, short or long words, or a digital readout. The days and the hours show only while there are any, so the last hour reads 59m 59s. Units and the words between them come from Intl in locale, en-US by default.

Preview width

Complete

onComplete is called once, when the countdown reaches zero: here, the button to ask for another code comes back. target can be a Date, a timestamp, or a date string.

Preview width

Accessibility

It renders a <time> element whose dateTime is the time left as a duration (P3DT4H5M9S). Screen readers read the time left in words (3 days, 4 hours, 5 minutes, 9 seconds) from a visually hidden copy, whatever the format; the moving digits are hidden from them. It doesn't announce each second, which would be noise; to mark it as a running timer, put it in an element with role="timer", and announce the end yourself from onComplete if it matters. With reduced motion the digits change in place.

FAQ

Why is it blank for a moment when the page loads?

The time left depends on the visitor's clock, which the server can't know, so it renders an invisible placeholder as wide as a countdown with every unit, and the browser fills in the time as soon as the page runs. The server and the browser render the same markup.

What can I pass as the target?

A Date, a timestamp in milliseconds, or a date string such as "2027-01-01T00:00:00Z". Give the string a time zone (the Z, or an offset) so everyone counts down to the same moment.

When is onComplete called?

Once, as the countdown reaches zero, and at once when the page opens after target has passed. The countdown then stays at zero and its timer stops.

Does it round the seconds up or down?

Up, as a countdown should: with half a second left it still reads one second, and it reads zero only at the target itself.

Why did the days disappear?

Leading units with nothing left aren't shown, so a countdown with no days left reads 23h 59m 59s, and with no hours left 59m 59s. Minutes and seconds always show.

What happens with reduced motion?

The digits change in place, without moving.

API Reference

CountdownText

<time> data-slot="countdown-text"

PropType
targetDate | number | string

The moment it counts down to: a Date, a timestamp in milliseconds or a date string ("2027-01-01T00:00:00Z").

Type
Date | number | string
format?Format

How it's written: each number with its unit, narrow (3d 04h 05m 09s), short or long, or a digital readout (03:04:05:09).

Default
"narrow"
locale?string

The locale it's written in (its digits, units and the joins between them). Fixed by default, so the server and the browser write it the same way.

Default
"en-US"
onComplete?() → void

Called once, when the countdown reaches zero (at once if target has passed).

Returns
void
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

CSS variableType
--countdown-text-durationstring

A digit's drop into its window (400ms).

All 97 text effects