OpenSourceUIOpenSourceUIOpensource UI
GitHub—
  1. Home
  2. Components
  3. Calender
  4. Event Countdown Card
Back to CalenderComponents / Calender

Event Countdown Card

Free Calender React component — EventCountdownCard. MIT licensed, copy-paste ready for Next.js and Tailwind CSS.

Live countdown to an event — days, hours, and minutes update every minute. Editorial typography, no ticker gimmicks.

Preview

Countdown

Product launch

Tuesday, December 1

86

days

21

hrs

23

min

Setup

How to use

Free for personal and commercial use. No UI attribution required. Include the MIT copyright notice when copying component source into your project. Read the MIT license.

  1. 1

    Run in your terminal:

    $npm install clsx tailwind-merge lucide-react
  2. 2

    Copy lib/cn.ts below. Skip if you already have cn().

  3. 3

    Copy the code below and create components/calender/event-countdown-card.tsx in your project.

  4. 4

    Import and render:

    Example

    import { EventCountdownCard } from "@/components/calender/event-countdown-card";

    <EventCountdownCard title="Product launch" targetDate="2026-12-01T09:00:00" />

lib/cn.ts

1
2
3
4
5
6
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
 
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

components/calender/event-countdown-card.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
"use client";
 
import {
forwardRef,
useEffect,
useMemo,
useState,
type ComponentPropsWithoutRef,
} from "react";
 
import { cn } from "@/lib/cn";
 
function getCountdown(target: Date) {
const diff = Math.max(0, target.getTime() - Date.now());
const totalMinutes = Math.floor(diff / 60_000);
const days = Math.floor(totalMinutes / (60 * 24));
const hours = Math.floor((totalMinutes % (60 * 24)) / 60);
const minutes = totalMinutes % 60;
return { days, hours, minutes, done: diff <= 0 };
}
 
export type EventCountdownCardProps = Readonly<
{
title?: string;
targetDate?: string;
onComplete?: () => void;
} & ComponentPropsWithoutRef<"div">
>;
 
// Event countdown — live days, hours, and minutes until a target date.
export const EventCountdownCard = forwardRef<
HTMLDivElement,
EventCountdownCardProps
>(
(
{
className,
title = "Product launch",
targetDate = "2026-12-01T09:00:00",
onComplete,
...props
},
ref,
) => {
const target = useMemo(() => new Date(targetDate), [targetDate]);
const [countdown, setCountdown] = useState(() => getCountdown(target));
 
useEffect(() => {
const tick = () => {
const next = getCountdown(target);
setCountdown(next);
if (next.done) onComplete?.();
};
tick();
const timer = globalThis.setInterval(tick, 60_000);
return () => globalThis.clearInterval(timer);
}, [target, onComplete]);
 
const dateLabel = target.toLocaleDateString("en-US", {
weekday: "long",
month: "long",
day: "numeric",
});
 
return (
<div
ref={ref}
data-slot="event-countdown-card"
className={cn(
"w-56 rounded-2xl border border-neutral-100 bg-white p-5 font-sans shadow-lg shadow-black/5 select-none",
className,
)}
{...props}
>
<p className="text-[11px] font-medium tracking-wide text-neutral-400 uppercase">
Countdown
</p>
<p className="mt-1 text-sm font-semibold text-neutral-900">{title}</p>
<p className="mt-0.5 text-xs text-neutral-500">{dateLabel}</p>
 
<div className="mt-5 grid grid-cols-3 gap-2 border-t border-neutral-100 pt-4">
{[
{ value: countdown.days, label: "days" },
{ value: countdown.hours, label: "hrs" },
{ value: countdown.minutes, label: "min" },
].map((unit) => (
<div key={unit.label} className="text-center">
<p
key={`${unit.label}-${unit.value}`}
className="text-2xl leading-none font-light text-neutral-900 tabular-nums opacity-100 starting:opacity-0 starting:translate-y-1 transition-all duration-500 ease-[cubic-bezier(0.25,0.46,0.45,0.94)]"
>
{String(unit.value).padStart(2, "0")}
</p>
<p className="mt-1 text-[10px] text-neutral-400 uppercase">
{unit.label}
</p>
</div>
))}
</div>
</div>
);
},
);
 
EventCountdownCard.displayName = "EventCountdownCard";
PreviousNext

On this

page

Jump to a section on this page.

  • lib/cn.ts
  • components/calender/event-countdown-card.tsx