A live countdown timer to a future moment — it re-renders every second, ticks down, never goes negative, and fires an onComplete callback once when it reaches zero. Use it wherever you're waiting on a deadline: a product/waitlist launch or "coming soon" page, a sale/offer/flash-deal or cart-reservation expiry, an OTP/verification resend or rate-limit cooldown, an auction or bid close, a webinar/event/stream start time, a maintenance window, a booking or checkout hold, or a quiz/game round timer. Common asks it answers: "countdown timer react", "react countdown component", "countdown to a date react", "days hours minutes seconds react", "sale ends in timer", "flash sale countdown", "launch countdown component", "coming soon countdown", "auction ending timer", "otp resend countdown", "resend code in 30 seconds", "cooldown timer react", "event starts in countdown", "react-countdown alternative", "useCountdown hook", "countdown timer without a date library", "shadcn countdown", "shadcn timer component", "countdown timer tailwind". Pass `to` as a Date, an ISO string, or epoch milliseconds. By default it renders labeled days/hours/minutes/seconds segments (the days block appears only once at least a day remains, or force it with showDays) using shadcn card/border/foreground tokens with tabular-nums so digits don't jitter. For a fully custom face — a compact "02:14:33", a circular ring, marketing hero digits — pass a render-prop child that receives { days, hours, minutes, seconds, total, isComplete } and return your own markup. The reason to install one rather than write a setInterval is that the obvious version counts instead of looking. Decrementing a stored number once a second drifts, and it drifts in the direction that matters: a browser throttles a background tab's timers to once a minute or slower, so a tab left open on a sale page comes back reading minutes high and claims time that is already gone. Every tick here recomputes from the target and a fresh Date.now(), so a throttled tab, a laptop waking from sleep and a slow frame all self-correct on the next tick rather than accumulating. onComplete is keyed to the deadline it fired for, so changing the interval — or any re-render that re-arms the timer — cannot fire it twice for the same moment, while giving it a new `to` still can; an invalid date renders nothing instead of NaN. It is SSR/hydration-safe (server and first client render agree, then a real clock takes over) and accessible: role=timer with an aria-atomic sr-only sentence ("2 days, 14 hours, 33 minutes remaining") while the visual segments are aria-hidden, so a screen reader can read the state on demand without being spammed each second. Tune the tick with interval (100 for smooth, 60000 for minute-only) and swap the finished view with completedLabel. shadcn/ui ships no countdown or timer component of any kind. Within pulld it is the future-facing counterpart to time-ago, which labels a moment in the past; it is distinct from idle-timeout, whose deadline moves every time the user does something, where this one runs to a fixed moment everybody shares; and the span it counts down is the value duration-input collects. Depends only on your cn util — no date library, no extra packages.
pnpm dlx shadcn@latest add "https://pulld.pages.dev/r/countdown.json"