useCountdown
A countdown hook for deadlines, launches, reservations, and temporary offers. It updates on the next clock-second boundary, returns a display-ready value, and continues tracking elapsed time after a deadline passes.
Last updated August 12, 2026
Copied from initial implementation supplied by a project contributor.
- Replaced a drifting interval with second-boundary-aligned timeouts.
- Added invalid-date handling so null and malformed targets safely return null.
- Changed rounded remaining time to ceiling semantics so a future target does not show zero early.
- Added documented result types and examples for formatted, segmented, paused, overdue, and RTL countdowns.
Overview
Installation
$ npx shadcn@latest add https://ui.persian-labs.ir/r/use-countdown.jsonUsage
Pass an ISO-8601 target date. The returned value is initially null until the hook runs in the browser, which keeps server rendering stable.
import { useCountdown } from "@/hooks/use-countdown"
function SaleCountdown({ endsAt }: { endsAt: string }) {
const countdown = useCountdown(endsAt)
if (!countdown) return null
return (
<p>
{countdown.isOverdue
? "This offer has ended"
: "Offer ends in " + countdown.formatted}
</p>
)
}Use a UTC ISO string from your API or database. Passing null or an invalid date disables the timer.
const endsAt = "2026-12-31T23:59:59.000Z"
const countdown = useCountdown(isTimerActive ? endsAt : null)Examples
Time units
Use the individual units when each part needs its own visual treatment. Hours can exceed 24.
Pause and play
Pause a countdown without losing its remaining duration, then resume it from exactly where it stopped.
Overdue targets
After the target passes, formatted remains absolute while totalSeconds is negative and isOverdue becomes true.
OTP resend
Keep the resend action unavailable until the cooldown expires, then start a fresh countdown whenever another code is sent.
RTL
Keep the time output LTR so separators and digits remain easy to scan inside Persian content.