Installation

pnpm dlx shadcn@latest add @cortexcn/calendarUsage
import { Calendar, CalendarDayButton } from "@/components/calendar";Examples
Single
import * as React from "react"import { Calendar } from "@/components/calendar"import { Card, CardContent } from "@/components/card"export function CalendarSingle() { const [date, setDate] = React.useState<Date | undefined>( new Date(new Date().getFullYear(), new Date().getMonth(), 12) ) return ( <div className="flex flex-col gap-6"> <Card className="mx-auto w-fit p-0"> <CardContent className="p-0"> <Calendar mode="single" selected={date} onSelect={setDate} captionLayout="dropdown" /> </CardContent> </Card> </div> )}Multiple
import { Calendar } from "@/components/calendar"import { Card, CardContent } from "@/components/card"export function CalendarMultiple() { return ( <div className="flex flex-col gap-6"> <Card className="mx-auto w-fit p-0"> <CardContent className="p-0"> <Calendar mode="multiple" /> </CardContent> </Card> </div> )}Week numbers
import * as React from "react"import { Calendar } from "@/components/calendar"import { Card, CardContent } from "@/components/card"export function CalendarWeekNumbers() { const [date, setDate] = React.useState<Date | undefined>( new Date(new Date().getFullYear(), 1, 3) ) return ( <div className="flex flex-col gap-6 justify-center"> <Card className="mx-auto w-fit p-0"> <CardContent className="p-0"> <Calendar mode="single" defaultMonth={date} selected={date} onSelect={setDate} showWeekNumber /> </CardContent> </Card> </div> )}Booked dates
import * as React from "react"import { Calendar } from "@/components/calendar"import { Card, CardContent } from "@/components/card"export function CalendarBookedDates() { const [date, setDate] = React.useState<Date | undefined>( new Date(new Date().getFullYear(), 1, 3) ) const bookedDates = Array.from( { length: 15 }, (_, i) => new Date(new Date().getFullYear(), 1, 12 + i) ) return ( <div className="flex flex-col gap-6"> <Card className="mx-auto w-fit p-0"> <CardContent className="p-0"> <Calendar mode="single" defaultMonth={date} selected={date} onSelect={setDate} disabled={bookedDates} modifiers={{ booked: bookedDates, }} modifiersClassNames={{ booked: "[&>button]:line-through opacity-100", }} /> </CardContent> </Card> </div> )}Range
import * as React from "react"import { addDays } from "date-fns"import { type DateRange } from "react-day-picker"import { Calendar } from "@/components/calendar"import { Card, CardContent } from "@/components/card"export function CalendarRange() { const [dateRange, setDateRange] = React.useState<DateRange | undefined>({ from: new Date(new Date().getFullYear(), 0, 12), to: addDays(new Date(new Date().getFullYear(), 0, 12), 30), }) return ( <div className="flex flex-col gap-6 p-12"> <Card className="mx-auto w-fit p-0"> <CardContent className="p-0"> <Calendar mode="range" defaultMonth={dateRange?.from} selected={dateRange} onSelect={setDateRange} numberOfMonths={2} disabled={(date) => date > new Date() || date < new Date("1900-01-01") } /> </CardContent> </Card> </div> )}Range multiple months
import * as React from "react"import { addDays } from "date-fns"import { type DateRange } from "react-day-picker"import { es } from "react-day-picker/locale"import { Calendar } from "@/components/calendar"import { Card, CardContent } from "@/components/card"export function CalendarRangeMultipleMonths() { const [range, setRange] = React.useState<DateRange | undefined>({ from: new Date(new Date().getFullYear(), 3, 12), to: addDays(new Date(new Date().getFullYear(), 3, 12), 60), }) return ( <div className="flex flex-col gap-6 p-12"> <Card className="mx-auto w-fit p-0"> <CardContent className="p-0"> <Calendar mode="range" defaultMonth={range?.from} selected={range} onSelect={setRange} numberOfMonths={3} locale={es} fixedWeeks /> </CardContent> </Card> </div> )}With time
import * as React from "react"import { Calendar } from "@/components/calendar"import { Card, CardContent, CardFooter } from "@/components/card"import { Field, FieldGroup, FieldLabel } from "@/components/field"import { InputGroup, InputGroupAddon, InputGroupInput } from "@/components/input-group"import { Clock2Icon } from "lucide-react"export function CalendarWithTime() { const [date, setDate] = React.useState<Date | undefined>( new Date(new Date().getFullYear(), new Date().getMonth(), 12) ) return ( <div className="flex flex-col gap-6"> <Card size="sm" className="mx-auto w-fit"> <CardContent> <Calendar mode="single" selected={date} onSelect={setDate} className="p-0" /> </CardContent> <CardFooter className="border-t bg-card"> <FieldGroup> <Field> <FieldLabel htmlFor="time-from">Start Time</FieldLabel> <InputGroup> <InputGroupInput id="time-from" type="time" step="1" defaultValue="10:30:00" className="appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none" /> <InputGroupAddon> <Clock2Icon className="text-muted-foreground" /> </InputGroupAddon> </InputGroup> </Field> <Field> <FieldLabel htmlFor="time-to">End Time</FieldLabel> <InputGroup> <InputGroupInput id="time-to" type="time" step="1" defaultValue="12:30:00" className="appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none" /> <InputGroupAddon> <Clock2Icon className="text-muted-foreground" /> </InputGroupAddon> </InputGroup> </Field> </FieldGroup> </CardFooter> </Card> </div> )}With presets
import * as React from "react"import { addDays } from "date-fns"import { Button } from "@/components/button"import { Calendar } from "@/components/calendar"import { Card, CardContent, CardFooter } from "@/components/card"export function CalendarWithPresets() { const [date, setDate] = React.useState<Date | undefined>( new Date(new Date().getFullYear(), 1, 12) ) const [currentMonth, setCurrentMonth] = React.useState<Date>( new Date(new Date().getFullYear(), new Date().getMonth(), 1) ) return ( <div className="flex flex-col gap-6"> <Card className="mx-auto w-fit max-w-[300px]" size="sm"> <CardContent className="px-4"> <Calendar mode="single" selected={date} onSelect={setDate} month={currentMonth} onMonthChange={setCurrentMonth} fixedWeeks className="p-0 [--cell-size:--spacing(9.5)]" /> </CardContent> <CardFooter className="flex flex-wrap gap-2 border-t"> {[ { label: "Today", value: 0 }, { label: "Tomorrow", value: 1 }, { label: "In 3 days", value: 3 }, { label: "In a week", value: 7 }, { label: "In 2 weeks", value: 14 }, ].map((preset) => ( <Button key={preset.value} variant="outline" size="sm" className="flex-1" onClick={() => { const newDate = addDays(new Date(), preset.value) setDate(newDate) setCurrentMonth( new Date(newDate.getFullYear(), newDate.getMonth(), 1) ) }} > {preset.label} </Button> ))} </CardFooter> </Card> </div> )}Custom days
import * as React from "react"import { addDays } from "date-fns"import { type DateRange } from "react-day-picker"import { Calendar, CalendarDayButton } from "@/components/calendar"import { Card, CardContent } from "@/components/card"export function CalendarCustomDays() { const [range, setRange] = React.useState<DateRange | undefined>({ from: new Date(new Date().getFullYear(), 11, 8), to: addDays(new Date(new Date().getFullYear(), 11, 8), 10), }) return ( <div className="flex flex-col gap-6"> <Card className="mx-auto w-fit p-0"> <CardContent className="p-0"> <Calendar mode="range" defaultMonth={range?.from} selected={range} onSelect={setRange} numberOfMonths={1} captionLayout="dropdown" className="[--cell-size:--spacing(10)] md:[--cell-size:--spacing(12)]" formatters={{ formatMonthDropdown: (date) => { return date.toLocaleString("default", { month: "long" }) }, }} components={{ DayButton: ({ children, modifiers, day, ...props }) => { const isWeekend = day.date.getDay() === 0 || day.date.getDay() === 6 return ( <CalendarDayButton day={day} modifiers={modifiers} {...props}> {children} {!modifiers.outside && ( <span>{isWeekend ? "$120" : "$100"}</span> )} </CalendarDayButton> ) }, }} /> </CardContent> </Card> </div> )}Date picker simple
import * as React from "react"import { format } from "date-fns"import { Button } from "@/components/button"import { Calendar } from "@/components/calendar"import { Field, FieldLabel } from "@/components/field"import { Popover, PopoverContent, PopoverTrigger } from "@/components/popover"import { CalendarIcon } from "lucide-react"export function DatePickerSimple() { const [date, setDate] = React.useState<Date>() return ( <div className="flex flex-col gap-6"> <Field className="mx-auto w-72"> <FieldLabel htmlFor="date-picker-simple">Date</FieldLabel> <Popover> <PopoverTrigger asChild> <Button variant="outline" id="date-picker-simple" className="justify-start px-2.5 font-normal" > <CalendarIcon data-icon="inline-start" /> {date ? format(date, "PPP") : <span>Pick a date</span>} </Button> </PopoverTrigger> <PopoverContent className="w-auto p-0" align="start"> <Calendar mode="single" selected={date} onSelect={setDate} /> </PopoverContent> </Popover> </Field> </div> )}Date picker with dropdowns
import * as React from "react"import { format } from "date-fns"import { Button } from "@/components/button"import { Calendar } from "@/components/calendar"import { Field, FieldLabel } from "@/components/field"import { Popover, PopoverContent, PopoverTrigger } from "@/components/popover"import { ChevronDownIcon } from "lucide-react"export function DataPickerWithDropdowns() { const [date, setDate] = React.useState<Date>() const [open, setOpen] = React.useState(false) return ( <div className="flex flex-col gap-6"> <Field className="mx-auto w-72"> <Popover open={open} onOpenChange={setOpen}> <FieldLabel htmlFor="date-picker-with-dropdowns-desktop"> Date </FieldLabel> <PopoverTrigger asChild> <Button variant="outline" id="date-picker-with-dropdowns-desktop" className="justify-start px-2.5 font-normal" > {date ? format(date, "PPP") : <span>Pick a date</span>} <ChevronDownIcon data-icon="inline-start" className="ml-auto" /> </Button> </PopoverTrigger> <PopoverContent className="w-auto p-0" align="start"> <Calendar mode="single" selected={date} onSelect={setDate} captionLayout="dropdown" /> <div className="flex gap-2 border-t p-2"> <Button variant="outline" size="sm" className="w-full" onClick={() => setOpen(false)} > Done </Button> </div> </PopoverContent> </Popover> </Field> </div> )}Date picker range
import * as React from "react"import { addDays, format } from "date-fns"import { type DateRange } from "react-day-picker"import { Button } from "@/components/button"import { Calendar } from "@/components/calendar"import { Field, FieldLabel } from "@/components/field"import { Popover, PopoverContent, PopoverTrigger } from "@/components/popover"import { CalendarIcon } from "lucide-react"export function DatePickerWithRange() { const [date, setDate] = React.useState<DateRange | undefined>({ from: new Date(new Date().getFullYear(), 0, 20), to: addDays(new Date(new Date().getFullYear(), 0, 20), 20), }) return ( <div className="flex flex-col gap-6"> <Field className="mx-auto w-72"> <FieldLabel htmlFor="date-picker-range">Date Picker Range</FieldLabel> <Popover> <PopoverTrigger asChild> <Button id="date-picker-range" variant="outline" className="justify-start px-2.5 font-normal" > <CalendarIcon data-icon="inline-start" /> {date?.from ? ( date.to ? ( <> {format(date.from, "LLL dd, y")} -{" "} {format(date.to, "LLL dd, y")} </> ) : ( format(date.from, "LLL dd, y") ) ) : ( <span>Pick a date</span> )} </Button> </PopoverTrigger> <PopoverContent className="w-auto p-0" align="start"> <Calendar mode="range" defaultMonth={date?.from} selected={date} onSelect={setDate} numberOfMonths={2} /> </PopoverContent> </Popover> </Field> </div> )}In card
import { Calendar } from "@/components/calendar"import { Card, CardContent } from "@/components/card"export function CalendarInCard() { return ( <div className="flex flex-col gap-6"> <Card className="mx-auto w-fit p-0"> <CardContent className="p-0"> <Calendar mode="single" /> </CardContent> </Card> </div> )}In popover
import { Button } from "@/components/button"import { Calendar } from "@/components/calendar"import { Popover, PopoverContent, PopoverTrigger } from "@/components/popover"import { CalendarIcon } from "lucide-react"export function CalendarInPopover() { return ( <div className="flex flex-col gap-6"> <Popover> <PopoverTrigger asChild> <Button variant="outline" className="px-2.5 font-normal"> <CalendarIcon data-icon="inline-start" /> Open Calendar </Button> </PopoverTrigger> <PopoverContent className="w-auto p-0" align="start"> <Calendar mode="single" /> </PopoverContent> </Popover> </div> )}Adapted from the shadcn/ui Calendar (MIT).
