Componentmedia0.97 KB gzipZero dependencies
Countdown
Live ticking event timer with segmented cards.
Preview
Live Preview
00DAYS
:00HOURS
:00MINS
:00SECS
Installation
Tokens guide →Add to your project via CLI (zero dependencies, code you own):
npx bigbullui add countdownOr install the full npm package:
npm install bigbulluiUsage
Open in StackBlitzimport { Countdown } from "@/components/ui/countdown";
<Countdown targetDate={new Date("2026-12-31T23:59:59")} />Usage guidance
Use Countdown when a ticket stub surface needs Live ticking event timer with segmented cards. Keep props minimal and prefer composition with sibling stubs.
Avoid Countdown for long form flows or dense data grids where a dedicated layout block fits better.
Source code
countdown.tsxZero dependencies (only React + utils). Copy and paste directly into your project:
"use client";
import * as React from "react";
import { cn } from "./lib/utils";
export interface CountdownProps extends React.HTMLAttributes<HTMLDivElement> {
targetDate: Date | string;
onComplete?: () => void;
className?: string;
}
export function Countdown({
targetDate,
onComplete,
className,
...props
}: CountdownProps) {
const calculateTimeLeft = React.useCallback(() => {
const difference = +new Date(targetDate) - +new Date();
if (difference <= 0) {
return { days: 0, hours: 0, minutes: 0, seconds: 0, completed: true };
}
return {
days: Math.floor(difference / (1000 * 60 * 60 * 24)),
hours: Math.floor((difference / (1000 * 60 * 60)) % 24),
minutes: Math.floor((difference / 1000 / 60) % 60),
seconds: Math.floor((difference / 1000) % 60),
completed: false,
};
}, [targetDate]);
const [timeLeft, setTimeLeft] = React.useState({
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
completed: false,
});
React.useEffect(() => {
setTimeLeft(calculateTimeLeft());
const timer = setInterval(() => {
const next = calculateTimeLeft();
setTimeLeft(next);
if (next.completed) {
clearInterval(timer);
onComplete?.();
}
}, 1000);
return () => clearInterval(timer);
}, [calculateTimeLeft, onComplete]);
const units = [
{ label: "DAYS", value: timeLeft.days },
{ label: "HOURS", value: timeLeft.hours },
{ label: "MINS", value: timeLeft.minutes },
{ label: "SECS", value: timeLeft.seconds },
];
return (
<div
role="timer"
aria-label="Countdown timer"
className={cn("inline-flex items-center gap-2 select-none", className)}
{...props}
>
{units.map((unit, idx) => (
<React.Fragment key={unit.label}>
<div className="flex flex-col items-center justify-center rounded-md border-2 border-foreground bg-card px-3 py-2 text-center shadow-xs min-w-[56px]">
<span className="font-mono text-xl font-black text-foreground">
{String(unit.value).padStart(2, "0")}
</span>
<span className="font-mono text-[9px] uppercase tracking-wider text-muted-foreground mt-0.5">
{unit.label}
</span>
</div>
{idx < units.length - 1 ? (
<span className="font-mono text-lg font-bold text-muted-foreground">:</span>
) : null}
</React.Fragment>
))}
</div>
);
}
Props
| Prop | Type | Description |
|---|---|---|
| targetDate | Date | string | |
| onComplete? | () => void | |
| className? | string |