Calendar

Data Display

Month 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