Calendar
A comprehensive calendar component for date selection with support for single dates, date ranges, and multiple date selection.
June 2025Jun 2025
function BasicCalendarExample() {
const [date, setDate] = React.useState<Date>();
return <Calendar selected={date} onSelect={setDate} />;
}
Installation
Install following dependencies:
npm install class-variance-authority lucide-react motion
pnpm add class-variance-authority lucide-react motion
yarn add class-variance-authority lucide-react motion
bun add class-variance-authority lucide-react motion
Copy and paste the following code into your project.
"use client";
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { motion, AnimatePresence } from "motion/react";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
const calendarVariants = cva(
"inline-block space-y-4 rounded-ele border border-border bg-background relative w-full max-w-sm mx-auto shadow-sm/2",
{
variants: {
size: {
sm: "p-2 sm:p-3 text-sm",
default: "p-3 sm:p-4",
lg: "p-4 sm:p-5 text-base",
},
alwaysOnTop: {
true: "z-9999",
false: "z-10",
},
},
defaultVariants: {
size: "default",
alwaysOnTop: true,
},
}
);
const dayVariants = cva(
"inline-flex h-8 w-8 sm:h-9 sm:w-9 items-center justify-center rounded-ele text-sm transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 cursor-pointer",
{
variants: {
variant: {
default:
"text-foreground hover:bg-accent hover:text-accent-foreground focus-visible:ring-ring",
selected:
"bg-primary text-primary-foreground hover:bg-primary/90 focus-visible:ring-ring font-semibold",
today:
"bg-accent text-accent-foreground font-semibold hover:bg-accent/80 focus-visible:ring-ring",
outside:
"text-muted-foreground opacity-50 hover:bg-accent hover:text-accent-foreground focus-visible:ring-ring",
disabled:
"text-muted-foreground opacity-30 cursor-not-allowed",
"range-start":
"bg-primary text-primary-foreground rounded-r-none hover:bg-primary/90 focus-visible:ring-ring",
"range-end":
"bg-primary text-primary-foreground rounded-l-none hover:bg-primary/90 focus-visible:ring-ring",
"range-middle":
"bg-primary/20 text-foreground rounded-none hover:bg-primary/30 focus-visible:ring-ring",
},
size: {
sm: "h-6 w-6 sm:h-7 sm:w-7 text-xs",
default: "h-8 w-8 sm:h-9 sm:w-9 text-sm",
lg: "h-9 w-9 sm:h-10 sm:w-10 text-base",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
interface CalendarProps extends VariantProps<typeof calendarVariants> {
selected?: Date;
onSelect?: (date: Date) => void;
disabled?: (date: Date) => boolean;
locale?: string;
className?: string;
showOutsideDays?: boolean;
minDate?: Date;
maxDate?: Date;
mode?: "single" | "multiple" | "range";
selectedDates?: Date[];
selectedRange?: { from: Date; to?: Date };
onSelectMultiple?: (dates: Date[]) => void;
onSelectRange?: (range: { from: Date; to?: Date }) => void;
showMonthYearPickers?: boolean;
alwaysOnTop?: boolean;
}
const DAYS_OF_WEEK = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const MONTHS = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
function Calendar({
selected,
onSelect,
disabled,
locale = "en-US",
className,
size,
showOutsideDays = true,
minDate,
maxDate,
mode = "single",
selectedDates = [],
selectedRange,
onSelectMultiple,
onSelectRange,
showMonthYearPickers = false,
alwaysOnTop = true,
...props
}: CalendarProps) {
const [currentDate, setCurrentDate] = React.useState(selected || new Date());
const [isAnimating, setIsAnimating] = React.useState(false);
const [direction, setDirection] = React.useState<"left" | "right">("right");
const today = new Date();
const currentMonth = currentDate.getMonth();
const currentYear = currentDate.getFullYear();
// Generate year range for year picker (current year ± 50 years)
const yearRange = Array.from({ length: 101 }, (_, i) => currentYear - 50 + i);
// Get first day of the month and calculate calendar grid
const firstDayOfMonth = new Date(currentYear, currentMonth, 1);
const lastDayOfMonth = new Date(currentYear, currentMonth + 1, 0);
const firstDayOfWeek = firstDayOfMonth.getDay();
const daysInMonth = lastDayOfMonth.getDate();
// Calculate previous month days to show
const prevMonthLastDay = new Date(currentYear, currentMonth, 0).getDate();
const prevMonthDays = Array.from(
{ length: firstDayOfWeek },
(_, i) => prevMonthLastDay - firstDayOfWeek + i + 1
);
// Calculate next month days to show
const totalCells = 42; // 6 rows × 7 days
const currentMonthDays = Array.from({ length: daysInMonth }, (_, i) => i + 1);
const remainingCells =
totalCells - prevMonthDays.length - currentMonthDays.length;
const nextMonthDays = Array.from({ length: remainingCells }, (_, i) => i + 1);
const navigateMonth = (direction: "prev" | "next") => {
setIsAnimating(true);
setDirection(direction === "prev" ? "left" : "right");
setTimeout(() => {
const newDate = new Date(currentDate);
if (direction === "prev") {
newDate.setMonth(currentMonth - 1);
} else {
newDate.setMonth(currentMonth + 1);
}
setCurrentDate(newDate);
setIsAnimating(false);
}, 150);
};
const handleMonthChange = (month: string) => {
const monthIndex = parseInt(month, 10);
const newDate = new Date(currentDate);
newDate.setMonth(monthIndex);
setCurrentDate(newDate);
};
const handleYearChange = (year: string) => {
const yearValue = parseInt(year, 10);
const newDate = new Date(currentDate);
newDate.setFullYear(yearValue);
setCurrentDate(newDate);
};
const isDateDisabled = (date: Date) => {
if (disabled?.(date)) return true;
if (minDate && date < minDate) return true;
if (maxDate && date > maxDate) return true;
return false;
};
const isDateSelected = (date: Date) => {
if (mode === "single") {
return selected && isSameDay(date, selected);
}
if (mode === "multiple") {
return selectedDates.some((d) => isSameDay(d, date));
}
if (mode === "range" && selectedRange) {
if (!selectedRange.to) {
// Only from date is selected
return isSameDay(date, selectedRange.from);
}
const dateTime = date.getTime();
const fromTime = selectedRange.from.getTime();
const toTime = selectedRange.to.getTime();
return dateTime >= fromTime && dateTime <= toTime;
}
return false;
};
const isDateInRange = (date: Date) => {
if (mode === "range" && selectedRange) {
if (!selectedRange.to) return isSameDay(date, selectedRange.from);
const dateTime = date.getTime();
const fromTime = selectedRange.from.getTime();
const toTime = selectedRange.to.getTime();
return dateTime > fromTime && dateTime < toTime;
}
return false;
};
const isRangeStart = (date: Date) => {
if (mode === "range" && selectedRange) {
return isSameDay(date, selectedRange.from);
}
return false;
};
const isRangeEnd = (date: Date) => {
if (mode === "range" && selectedRange && selectedRange.to) {
return isSameDay(date, selectedRange.to);
}
return false;
};
const isToday = (date: Date) => isSameDay(date, today);
const handleDateClick = (day: number, monthOffset: number = 0) => {
const clickedDate = new Date(currentYear, currentMonth + monthOffset, day);
if (isDateDisabled(clickedDate)) return;
if (mode === "single") {
onSelect?.(clickedDate);
} else if (mode === "multiple") {
const newDates = selectedDates.some((d) => isSameDay(d, clickedDate))
? selectedDates.filter((d) => !isSameDay(d, clickedDate))
: [...selectedDates, clickedDate];
onSelectMultiple?.(newDates);
} else if (mode === "range") {
if (!selectedRange || (selectedRange.from && selectedRange.to)) {
// Start new range selection - only set the 'from' date
onSelectRange?.({ from: clickedDate });
} else if (selectedRange.from && !selectedRange.to) {
// Complete the range selection
const from =
selectedRange.from <= clickedDate ? selectedRange.from : clickedDate;
const to =
selectedRange.from <= clickedDate ? clickedDate : selectedRange.from;
onSelectRange?.({ from, to });
}
}
};
const getDayVariant = (
day: number,
monthOffset: number = 0
):
| "default"
| "selected"
| "today"
| "outside"
| "disabled"
| "range-start"
| "range-end"
| "range-middle" => {
const date = new Date(currentYear, currentMonth + monthOffset, day);
if (isDateDisabled(date)) return "disabled";
if (mode === "range" && selectedRange) {
if (isRangeStart(date)) return "range-start";
if (isRangeEnd(date)) return "range-end";
if (isDateInRange(date)) return "range-middle";
}
if (isDateSelected(date)) return "selected";
if (isToday(date)) return "today";
if (monthOffset !== 0) return "outside";
return "default";
};
const slideVariants = {
enter: (direction: string) => ({
x: direction === "right" ? 300 : -300,
opacity: 0,
}),
center: {
zIndex: 1,
x: 0,
opacity: 1,
},
exit: (direction: string) => ({
zIndex: 0,
x: direction === "right" ? -300 : 300,
opacity: 0,
}),
};
return (
<div
className={cn(calendarVariants({ size, alwaysOnTop }), className)}
{...props}
>
{" "}
{/* Header */}
<div className="flex items-center justify-between">
<button
onClick={() => navigateMonth("prev")}
className="inline-flex items-center justify-center rounded-ele p-1 sm:p-1.5 transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
disabled={isAnimating}
>
<ChevronLeft className="h-4 w-4" />
</button>
<div className="flex items-center gap-1 sm:gap-2 min-w-0 flex-1 justify-center">
{showMonthYearPickers ? (
<div className="flex items-center gap-1 sm:gap-2 flex-wrap justify-center">
<Select
value={currentMonth.toString()}
onValueChange={handleMonthChange}
>
<SelectTrigger
className="w-[100px] sm:w-[120px] h-7 sm:h-8 text-xs sm:text-sm"
size="sm"
>
<SelectValue />
</SelectTrigger>
<SelectContent>
{MONTHS.map((month, index) => (
<SelectItem key={index} value={index.toString()}>
<span className="hidden sm:inline">{month}</span>
<span className="sm:hidden">{month.slice(0, 3)}</span>
</SelectItem>
))}
</SelectContent>
</Select>
<Select
value={currentYear.toString()}
onValueChange={handleYearChange}
>
<SelectTrigger
className="w-[70px] sm:w-[80px] h-7 sm:h-8 text-xs sm:text-sm"
size="sm"
>
<SelectValue />
</SelectTrigger>
<SelectContent>
{yearRange.map((year) => (
<SelectItem key={year} value={year.toString()}>
{year}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
) : (
<motion.h2
key={`${currentMonth}-${currentYear}`}
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
className="text-base sm:text-lg font-semibold text-foreground text-center px-2"
>
<span className="hidden sm:inline">
{MONTHS[currentMonth]} {currentYear}
</span>
<span className="sm:hidden">
{MONTHS[currentMonth].slice(0, 3)} {currentYear}
</span>
</motion.h2>
)}
</div>
<button
onClick={() => navigateMonth("next")}
className="inline-flex items-center justify-center rounded-ele p-1 sm:p-1.5 transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
disabled={isAnimating}
>
<ChevronRight className="h-4 w-4" />
</button>
</div>
{/* Days of week header */}
<div className="grid grid-cols-7 gap-1">
{DAYS_OF_WEEK.map((day) => (
<div
key={day}
className="flex items-center justify-center h-7 sm:h-8 text-xs text-muted-foreground"
>
<span className="hidden sm:inline">{day}</span>
<span className="sm:hidden">{day.slice(0, 1)}</span>
</div>
))}
</div>
{/* Calendar grid */}
<div className="relative overflow-hidden">
<AnimatePresence mode="wait" custom={direction}>
<motion.div
key={`${currentMonth}-${currentYear}`}
custom={direction}
variants={slideVariants}
initial="enter"
animate="center"
exit="exit"
transition={{
x: { type: "spring", stiffness: 500, damping: 30 },
opacity: { duration: 0.2 },
}}
className="grid grid-cols-7 gap-0.5 sm:gap-1"
>
{/* Previous month days */}
{showOutsideDays &&
prevMonthDays.map((day) => (
<button
key={`prev-${day}`}
onClick={() => handleDateClick(day, -1)}
className={cn(
dayVariants({ variant: getDayVariant(day, -1), size })
)}
disabled={isDateDisabled(
new Date(currentYear, currentMonth - 1, day)
)}
>
{day}
</button>
))}
{/* Current month days */}
{currentMonthDays.map((day) => (
<button
key={`current-${day}`}
onClick={() => handleDateClick(day)}
className={cn(
dayVariants({ variant: getDayVariant(day), size })
)}
disabled={isDateDisabled(
new Date(currentYear, currentMonth, day)
)}
>
{day}
</button>
))}
{/* Next month days */}
{showOutsideDays &&
nextMonthDays.map((day) => (
<button
key={`next-${day}`}
onClick={() => handleDateClick(day, 1)}
className={cn(
dayVariants({ variant: getDayVariant(day, 1), size })
)}
disabled={isDateDisabled(
new Date(currentYear, currentMonth + 1, day)
)}
>
{day}
</button>
))}
</motion.div>
</AnimatePresence>
</div>
</div>
);
}
// Helper function to check if two dates are the same day
function isSameDay(date1: Date, date2: Date): boolean {
return (
date1.getDate() === date2.getDate() &&
date1.getMonth() === date2.getMonth() &&
date1.getFullYear() === date2.getFullYear()
);
}
export { Calendar, calendarVariants, dayVariants, type CalendarProps };
npx hextaui@latest add calendar
pnpm dlx hextaui@latest add calendar
yarn dlx hextaui@latest add calendar
bun x hextaui@latest add calendar
Usage
import { Calendar } from "@/components/ui/Calendar";
<Calendar selected={selectedDate} onSelect={setSelectedDate} />
Examples
Disabled Dates
June 2025Jun 2025
export function CalendarDisabledDatesExample() {
const [date, setDate] = React.useState<Date>();
const disableWeekends = (date: Date) => {
return date.getDay() === 0 || date.getDay() === 6; // Disable weekends
};
return (
<Calendar selected={date} onSelect={setDate} disabled={disableWeekends} />
);
}
With Month/Year Dropdowns
function CalendarWithSelectorsExample() {
const [date, setDate] = React.useState<Date>();
return (
<Calendar
selected={date}
onSelect={setDate}
showMonthYearPickers={true}
/>
);
}
Date Range Selection
June 2025Jun 2025
function CalendarRangeExample() {
const [range, setRange] = React.useState<{ from: Date; to?: Date }>();
return (
<Calendar
mode="range"
selectedRange={range}
onSelectRange={setRange}
/>
);
}
Multiple Date Selection
June 2025Jun 2025
function CalendarMultipleExample() {
const [dates, setDates] = React.useState<Date[]>([]);
return (
<Calendar
mode="multiple"
selectedDates={dates}
onSelectMultiple={setDates}
/>
);
}
Calendar Sizes
Small Calendar
June 2025Jun 2025
Default Calendar
June 2025Jun 2025
Large Calendar
June 2025Jun 2025
function CalendarSizesExample() {
const [date, setDate] = React.useState<Date>();
return (
<div className="space-y-6">
<div>
<h4 className="text-sm font-medium mb-2">Small Calendar</h4>
<Calendar selected={date} onSelect={setDate} size="sm" />
</div>
<div>
<h4 className="text-sm font-medium mb-2">Default Calendar</h4>
<Calendar selected={date} onSelect={setDate} size="default" />
</div>
<div>
<h4 className="text-sm font-medium mb-2">Large Calendar</h4>
<Calendar selected={date} onSelect={setDate} size="lg" />
</div>
</div>
);
}
Booking System Calendar
Perfect for reservation systems, hotels, or appointment booking.
Hotel Booking Calendar
Select an available date for your reservation. Grayed out dates are unavailable.
June 2025Jun 2025
function BookingCalendarExample() {
const [selectedDate, setSelectedDate] = React.useState<Date>();
// Mock booked dates (in a real app, this would come from your API)
const bookedDates = [
new Date(2025, 5, 15), // June 15, 2025
new Date(2025, 5, 16), // June 16, 2025
new Date(2025, 5, 20), // June 20, 2025
new Date(2025, 5, 25), // June 25, 2025
];
const isDateBooked = (date: Date) => {
return bookedDates.some(bookedDate =>
date.toDateString() === bookedDate.toDateString()
);
};
const disableBookedDates = (date: Date) => {
// Disable past dates and booked dates
const today = new Date();
today.setHours(0, 0, 0, 0);
return date < today || isDateBooked(date);
};
return (
<div className="space-y-4">
<div>
<h4 className="text-sm font-medium mb-2">Hotel Booking Calendar</h4>
<p className="text-xs text-muted-foreground mb-4">
Select an available date for your reservation. Grayed out dates are unavailable.
</p>
</div>
<Calendar
selected={selectedDate}
onSelect={setSelectedDate}
disabled={disableBookedDates}
minDate={new Date()}
maxDate={new Date(new Date().setMonth(new Date().getMonth() + 6))}
/>
{selectedDate && (
<p className="text-sm text-green-600">
✓ Selected: {selectedDate.toLocaleDateString()}
</p>
)}
</div>
);
}
Vacation Planner
Great for travel planning with range selection and day calculation.
Plan Your Vacation
Select your vacation start and end dates
function VacationPlannerExample() {
const [vacationRange, setVacationRange] = React.useState<{ from: Date; to?: Date }>();
const calculateDays = () => {
if (vacationRange?.from && vacationRange?.to) {
const diffTime = Math.abs(vacationRange.to.getTime() - vacationRange.from.getTime());
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) + 1;
return diffDays;
}
return 0;
};
return (
<div className="space-y-4">
<div>
<h4 className="text-sm font-medium mb-2">Plan Your Vacation</h4>
<p className="text-xs text-muted-foreground mb-4">
Select your vacation start and end dates
</p>
</div>
<Calendar
mode="range"
selectedRange={vacationRange}
onSelectRange={setVacationRange}
minDate={new Date()}
showMonthYearPickers={true}
/>
{vacationRange?.from && (
<div className="p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
<p className="text-sm">
<strong>Vacation Start:</strong> {vacationRange.from.toLocaleDateString()}
</p>
{vacationRange.to && (
<>
<p className="text-sm">
<strong>Vacation End:</strong> {vacationRange.to.toLocaleDateString()}
</p>
<p className="text-sm font-medium text-blue-600 dark:text-blue-400">
Total Days: {calculateDays()}
</p>
</>
)}
</div>
)}
</div>
);
}
Task Scheduler
Perfect for project management and recurring task scheduling.
Schedule Tasks
Select multiple dates for recurring tasks
June 2025Jun 2025
function TaskSchedulerExample() {
const [taskDates, setTaskDates] = React.useState<Date[]>([]);
const handleDateToggle = (dates: Date[]) => {
setTaskDates(dates);
};
return (
<div className="space-y-4">
<div>
<h4 className="text-sm font-medium mb-2">Schedule Tasks</h4>
<p className="text-xs text-muted-foreground mb-4">
Select multiple dates for recurring tasks
</p>
</div>
<Calendar
mode="multiple"
selectedDates={taskDates}
onSelectMultiple={handleDateToggle}
minDate={new Date()}
/>
{taskDates.length > 0 && (
<div className="p-3 bg-green-50 dark:bg-green-900/20 rounded-lg">
<p className="text-sm font-medium mb-2">Scheduled Task Dates:</p>
<div className="space-y-1">
{taskDates.map((date, index) => (
<p key={index} className="text-xs text-green-700 dark:text-green-300">
• {date.toLocaleDateString('en-US', {
weekday: 'short',
month: 'short',
day: 'numeric'
})}
</p>
))}
</div>
<p className="text-xs text-green-600 dark:text-green-400 mt-2">
Total: {taskDates.length} date{taskDates.length !== 1 ? 's' : ''} selected
</p>
</div>
)}
</div>
);
}
Event Calendar
Show only dates with events and allow selection of event dates.
Event Calendar
Only dates with events are selectable
June 2025Jun 2025
Upcoming Events:
function EventCalendarExample() {
const [eventDate, setEventDate] = React.useState<Date>();
// Mock events data
const events = [
{ date: new Date(2025, 5, 12), title: "Team Meeting" },
{ date: new Date(2025, 5, 18), title: "Product Launch" },
{ date: new Date(2025, 5, 24), title: "Conference" },
];
const hasEvent = (date: Date) => {
return events.some(event =>
date.toDateString() === event.date.toDateString()
);
};
const disableEventDays = (date: Date) => {
// Only allow selection of dates that have events
return !hasEvent(date);
};
const getEventForDate = (date: Date) => {
return events.find(event =>
date.toDateString() === event.date.toDateString()
);
};
return (
<div className="space-y-4">
<div>
<h4 className="text-sm font-medium mb-2">Event Calendar</h4>
<p className="text-xs text-muted-foreground mb-4">
Only dates with events are selectable
</p>
</div>
<Calendar
selected={eventDate}
onSelect={setEventDate}
disabled={disableEventDays}
/>
<div className="space-y-2">
<p className="text-sm font-medium">Upcoming Events:</p>
{events.map((event, index) => (
<div key={index} className="flex justify-between items-center p-2 bg-gray-50 dark:bg-gray-800 rounded">
<span className="text-sm">{event.title}</span>
<span className="text-xs text-muted-foreground">
{event.date.toLocaleDateString()}
</span>
</div>
))}
</div>
{eventDate && (
<div className="p-3 bg-purple-50 dark:bg-purple-900/20 rounded-lg">
<p className="text-sm">
<strong>Selected Event:</strong> {getEventForDate(eventDate)?.title}
</p>
<p className="text-xs text-muted-foreground">
{eventDate.toLocaleDateString()}
</p>
</div>
)}
</div>
);
}
Age Verification Calendar
Useful for forms requiring age verification with date restrictions.
Age Verification (18+)
Select your birth date. Must be 18 or older.
function AgeRestrictedCalendarExample() {
const [birthDate, setBirthDate] = React.useState<Date>();
const isUnder18 = (date: Date) => {
const today = new Date();
const eighteenYearsAgo = new Date(today.getFullYear() - 18, today.getMonth(), today.getDate());
return date > eighteenYearsAgo;
};
const calculateAge = (birthDate: Date) => {
const today = new Date();
let age = today.getFullYear() - birthDate.getFullYear();
const monthDiff = today.getMonth() - birthDate.getMonth();
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
age--;
}
return age;
};
return (
<div className="space-y-4">
<div>
<h4 className="text-sm font-medium mb-2">Age Verification (18+)</h4>
<p className="text-xs text-muted-foreground mb-4">
Select your birth date. Must be 18 or older.
</p>
</div>
<Calendar
selected={birthDate}
onSelect={setBirthDate}
disabled={isUnder18}
maxDate={new Date()}
showMonthYearPickers={true}
/>
{birthDate && (
<div className="p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
<p className="text-sm">
<strong>Birth Date:</strong> {birthDate.toLocaleDateString()}
</p>
<p className="text-sm">
<strong>Age:</strong> {calculateAge(birthDate)} years old
</p>
<p className={`text-sm font-medium ${calculateAge(birthDate) >= 18 ? 'text-green-600' : 'text-red-600'}`}>
{calculateAge(birthDate) >= 18 ? '✓ Eligible' : '✗ Must be 18 or older'}
</p>
</div>
)}
</div>
);
}
Responsive Design
Perfect for adapting to different screen sizes automatically.
Responsive Calendar
Calendar adapts to different screen sizes with optimized dropdowns and layout
function ResponsiveCalendarExample() {
const [date, setDate] = React.useState<Date>();
return (
<div className="w-full max-w-xs mx-auto">
<div className="mb-4">
<h4 className="text-sm font-medium mb-2">Responsive Calendar</h4>
<p className="text-xs text-muted-foreground mb-4">
Calendar adapts to different screen sizes with optimized dropdowns and layout
</p>
</div>
<Calendar
selected={date}
onSelect={setDate}
showMonthYearPickers={true}
className="w-full"
/>
{date && (
<div className="mt-4 p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
<p className="text-sm">
<strong>Selected:</strong> {date.toLocaleDateString()}
</p>
</div>
)}
</div>
);
}
Mobile-Optimized
Specifically designed for mobile devices with larger touch targets.
Mobile-Optimized Range Selection
Optimized for touch interactions with larger touch targets
function MobileCalendarExample() {
const [range, setRange] = React.useState<{ from: Date; to?: Date }>();
return (
<div className="w-full max-w-sm mx-auto">
<div className="mb-4">
<h4 className="text-sm font-medium mb-2">Mobile-Optimized Range Selection</h4>
<p className="text-xs text-muted-foreground mb-4">
Optimized for touch interactions with larger touch targets
</p>
</div>
<Calendar
mode="range"
selectedRange={range}
onSelectRange={setRange}
showMonthYearPickers={true}
size="default"
className="w-full"
/>
{range?.from && (
<div className="mt-4 p-3 bg-green-50 dark:bg-green-900/20 rounded-lg">
<p className="text-sm">
<strong>Start:</strong> {range.from.toLocaleDateString()}
</p>
{range.to && (
<p className="text-sm">
<strong>End:</strong> {range.to.toLocaleDateString()}
</p>
)}
</div>
)}
</div>
);
}
Props
Prop | Type | Default |
---|---|---|
className? | string | undefined |
locale? | string | "en-US" |
alwaysOnTop? | boolean | true |
showMonthYearPickers? | boolean | false |
showOutsideDays? | boolean | true |
maxDate? | Date | undefined |
minDate? | Date | undefined |
size? | "sm" | "default" | "lg" | "default" |
disabled? | (date: Date) => boolean | undefined |
onSelectRange? | (range: { from: Date; to: Date }) => void | undefined |
onSelectMultiple? | (dates: Date[]) => void | undefined |
selectedRange? | { from: Date; to: Date } | undefined |
selectedDates? | Date[] | [] |
mode? | "single" | "multiple" | "range" | "single" |
onSelect? | (date: Date) => void | undefined |
selected? | Date | undefined |
Last updated on