opensourceui
GitHubTwitter/X
  1. Home
  2. Components
  3. Calender
  4. Event Countdown Card
Back to Calender

Components / 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

142

days

11

hrs

00

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-200/80 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

Sponsor spot · demo preview

Logo

Your brand name

Your tagline or category

A short pitch about your product or service goes here. This is a preview of how sponsor cards will look in this sidebar.

Your call to action

This slot is available. Sponsor Opensource UI and reach developers browsing components every day.