Calendar
Data DisplayMonth calendar grid with day selection.
Preview
March 2026
Su
Mo
Tu
We
Th
Fr
Sa
Usage
example.jsx
import { Calendar } from "@/components/ui/calendar";
export default function Example() {
return <Calendar />;
}Source Code
Copy this file into components/ui/calendar.jsx in your project.
calendar.jsx
"use client";
import { forwardRef, useState } from "react";
import { cn } from "@/lib/utils";
const DAYS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
const MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
const Calendar = forwardRef(({ className, selected, onSelect, ...props }, ref) => {
const today = new Date();
const [viewDate, setViewDate] = useState(selected || today);
const year = viewDate.getFullYear();
const month = viewDate.getMonth();
const firstDay = new Date(year, month, 1).getDay();
const daysInMonth = new Date(year, month + 1, 0).getDate();
const days = [];
for (let i = 0; i < firstDay; i++) days.push(null);
for (let d = 1; d <= daysInMonth; d++) days.push(d);
const prev = () => setViewDate(new Date(year, month - 1, 1));
const next = () => setViewDate(new Date(year, month + 1, 1));
const isSelected = (d) => selected && selected.getFullYear() === year && selected.getMonth() === month && selected.getDate() === d;
const isToday = (d) => today.getFullYear() === year && today.getMonth() === month && today.getDate() === d;
return (
<div ref={ref} className={cn("w-fit rounded-md border bg-background p-3", className)} {...props}>
<div className="mb-2 flex items-center justify-between">
<button onClick={prev} className="rounded-md p-1 hover:bg-accent"><svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" /></svg></button>
<span className="text-sm font-medium">{MONTHS[month]} {year}</span>
<button onClick={next} className="rounded-md p-1 hover:bg-accent"><svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" /></svg></button>
</div>
<div className="grid grid-cols-7 gap-0">
{DAYS.map((d) => <div key={d} className="flex h-8 w-8 items-center justify-center text-xs font-medium text-muted-foreground">{d}</div>)}
{days.map((d, i) => (
<div key={i} className="flex h-8 w-8 items-center justify-center">
{d && (
<button onClick={() => onSelect?.(new Date(year, month, d))}
className={cn("h-8 w-8 rounded-md text-sm transition-colors hover:bg-accent",
isSelected(d) && "bg-primary text-primary-foreground hover:bg-primary/90",
isToday(d) && !isSelected(d) && "border border-primary text-primary"
)}
>{d}</button>
)}
</div>
))}
</div>
</div>
);
});
Calendar.displayName = "Calendar";
export { Calendar };
Quick Install
Make sure you have the cn() utility set up. It requires clsx and tailwind-merge.
npm install clsx tailwind-merge