shadcn.io is not affiliated with official shadcn/ui
Shadcn Calendar for React and Tailwind
Select a date, dates, or a range from an inline calendar.
September 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
30 | 31 | 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 | 1 | 2 | 3 |
Installation
bunx --bun shadcn@latest add https://kit.dev/r/calendar.jsonpnpm dlx shadcn@latest add https://kit.dev/r/calendar.jsonnpx shadcn@latest add https://kit.dev/r/calendar.jsonyarn shadcn@latest add https://kit.dev/r/calendar.jsonThis component depends on Button and Native Select. Install them first if you haven't already.
Install the following dependencies:
bun add @ark-ui/react lucide-reactpnpm add @ark-ui/react lucide-reactnpm install @ark-ui/react lucide-reactyarn add @ark-ui/react lucide-reactCopy and paste the following code into your project.
"use client";
import {
DatePicker as ArkCalendar,
useDatePicker as useArkDatePicker,
useDatePickerContext as useArkDatePickerContext,
} from "@ark-ui/react/date-picker";
import {
ChevronDownIcon,
ChevronLeftIcon,
ChevronRightIcon,
} from "lucide-react";
import type React from "react";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { nativeSelectVariants } from "@/components/ui/native-select";
export const useCalendar = useArkDatePicker;
export const useCalendarContext = useArkDatePickerContext;
export const CalendarContext: typeof ArkCalendar.Context = ArkCalendar.Context;
const calendarRootClassName = cn("[--cell-size:--spacing(9)]", "w-fit");
export const Calendar = (
props: React.ComponentProps<typeof ArkCalendar.Root>
) => {
const { lazyMount = true, unmountOnExit = true, className, ...rest } = props;
return (
<ArkCalendar.Root
className={cn(calendarRootClassName, className)}
data-slot="calendar"
inline
lazyMount={lazyMount}
unmountOnExit={unmountOnExit}
{...rest}
/>
);
};
export const CalendarRootProvider = (
props: React.ComponentProps<typeof ArkCalendar.RootProvider>
) => {
const { lazyMount = true, unmountOnExit = true, className, ...rest } = props;
return (
<ArkCalendar.RootProvider
className={cn(calendarRootClassName, className)}
data-slot="calendar"
lazyMount={lazyMount}
unmountOnExit={unmountOnExit}
{...rest}
/>
);
};
export const CalendarControl = (
props: React.ComponentProps<typeof ArkCalendar.Control>
) => (
<ArkCalendar.Control
className="inline-flex items-center gap-2"
data-slot="calendar-control"
{...props}
/>
);
export const CalendarLabel = (
props: React.ComponentProps<typeof ArkCalendar.Label>
) => (
<ArkCalendar.Label
className="font-medium text-sm"
data-slot="calendar-label"
{...props}
/>
);
export const CalendarTrigger = (
props: React.ComponentProps<typeof ArkCalendar.Trigger>
) => <ArkCalendar.Trigger data-slot="calendar-trigger" {...props} />;
export const CalendarPresetTrigger = (
props: React.ComponentProps<typeof ArkCalendar.PresetTrigger>
) => (
<ArkCalendar.PresetTrigger data-slot="calendar-preset-trigger" {...props} />
);
export const CalendarViewDate = (
props: React.ComponentProps<typeof ArkCalendar.RangeText>
) => {
const { className, ...rest } = props;
return (
<ArkCalendar.RangeText
className={cn("font-medium text-sm", className)}
data-slot="calendar-range-text"
{...rest}
/>
);
};
export const CalendarTodayTrigger = (
props: React.ComponentProps<typeof Button>
) => {
const { variant = "outline", size = "lg", ...rest } = props;
return (
<CalendarContext>
{(calendar) => {
const handleSelectToday = () => {
calendar.selectToday();
};
return (
<Button
data-slot="calendar-today-trigger"
onClick={handleSelectToday}
size={size}
variant={variant}
{...rest}
>
Today
</Button>
);
}}
</CalendarContext>
);
};
export const CalendarClearTrigger = (
props: React.ComponentProps<typeof ArkCalendar.ClearTrigger>
) => <ArkCalendar.ClearTrigger data-slot="calendar-clear-trigger" {...props} />;
export const CalendarYearSelect = (
props: React.ComponentProps<typeof ArkCalendar.YearSelect>
) => {
const { className, ...rest } = props;
return (
<div
className={cn("relative w-fit has-[select:disabled]:opacity-64")}
data-slot="calendar-year-select-wrapper"
>
<ArkCalendar.YearSelect
className={cn(nativeSelectVariants())}
data-slot="calendar-year-select"
{...rest}
/>
<ChevronDownIcon
aria-hidden="true"
className={cn(
"absolute inset-e-2.5 top-1/2 -translate-y-1/2",
"size-4",
"select-none text-muted-foreground",
"pointer-events-none"
)}
data-slot="calendar-year-select-icon"
/>
</div>
);
};
export const CalendarMonthSelect = (
props: React.ComponentProps<typeof ArkCalendar.MonthSelect>
) => {
const { className, ...rest } = props;
return (
<div
className={cn("relative w-fit has-[select:disabled]:opacity-64")}
data-slot="calendar-month-select-wrapper"
>
<ArkCalendar.MonthSelect
className={cn(nativeSelectVariants(), className)}
data-slot="calendar-month-select"
{...rest}
/>
<ChevronDownIcon
aria-hidden="true"
className={cn(
"absolute inset-e-2.5 top-1/2 -translate-y-1/2",
"size-4",
"select-none text-muted-foreground",
"pointer-events-none"
)}
data-slot="calendar-month-select-icon"
/>
</div>
);
};
export const CalendarView = (
props: React.ComponentProps<typeof ArkCalendar.View>
) => {
const { className, ...rest } = props;
return (
<ArkCalendar.View
className={cn("flex flex-col gap-1", className)}
data-slot="calendar-view"
{...rest}
/>
);
};
export const CalendarViewTrigger = (
props: React.ComponentProps<typeof ArkCalendar.ViewTrigger>
) => {
const { className, children, ...rest } = props;
return (
<ArkCalendar.ViewTrigger
className={cn(
"rounded-md px-2 py-1",
"font-medium text-sm",
"outline-none hover:bg-accent",
"focus-visible:border-primary focus-visible:ring-[3px] focus-visible:ring-ring/32",
className
)}
data-slot="calendar-view-trigger"
{...rest}
>
{children ?? <CalendarViewDate />}
</ArkCalendar.ViewTrigger>
);
};
export const CalendarViewControl = (
props: React.ComponentProps<typeof ArkCalendar.ViewControl>
) => {
const { className, ...rest } = props;
return (
<ArkCalendar.ViewControl
className={cn(
"relative",
"h-auto w-full",
"flex items-center gap-1.5",
className
)}
data-slot="calendar-view-control"
{...rest}
/>
);
};
export const CalendarPrevTrigger = (
props: React.ComponentProps<typeof ArkCalendar.PrevTrigger>
) => (
<ArkCalendar.PrevTrigger asChild data-slot="calendar-prev-trigger" {...props}>
<Button
aria-label="Previous"
className="me-auto"
size="icon-md"
variant="ghost"
>
<ChevronLeftIcon aria-hidden className="rtl:rotate-180" />
</Button>
</ArkCalendar.PrevTrigger>
);
export const CalendarNextTrigger = (
props: React.ComponentProps<typeof ArkCalendar.NextTrigger>
) => (
<ArkCalendar.NextTrigger asChild data-slot="calendar-next-trigger" {...props}>
<Button
aria-label="Next"
className="ms-auto"
size="icon-md"
variant="ghost"
>
<ChevronRightIcon aria-hidden className="rtl:rotate-180" />
</Button>
</ArkCalendar.NextTrigger>
);
export const CalendarTable = (
props: React.ComponentProps<typeof ArkCalendar.Table>
) => {
const { className, ...rest } = props;
return (
<ArkCalendar.Table
className={cn("group", "w-full min-w-60", "border-collapse", className)}
data-slot="calendar-table"
{...rest}
/>
);
};
interface CalendarWeekDaysProps
extends React.ComponentProps<typeof ArkCalendar.TableHead> {
/**
* The format of the week days
*
* @default 'narrow'
*/
format?: "narrow" | "short" | "long";
}
export const CalendarWeekDays = (props: CalendarWeekDaysProps) => {
const { format = "narrow", ...rest } = props;
return (
<CalendarContext>
{(calendar) => (
<CalendarTableHead data-slot="calendar-table-head" {...rest}>
<CalendarTableRow>
{calendar.showWeekNumbers ? (
<CalendarWeekNumberHeaderCell>Wk</CalendarWeekNumberHeaderCell>
) : null}
{calendar.weekDays.map((weekDay) => (
<CalendarTableHeader key={weekDay.short}>
{weekDay[format]}
</CalendarTableHeader>
))}
</CalendarTableRow>
</CalendarTableHead>
)}
</CalendarContext>
);
};
export const CalendarTableDays = (
props: React.ComponentProps<typeof CalendarTableBody>
) => {
const { tabIndex, ...rest } = props;
return (
<CalendarContext>
{(calendar) => (
<CalendarTableBody {...rest}>
{calendar.weeks.map((week, index) => (
<CalendarTableRow key={week.map((day) => day.toString()).join("-")}>
{calendar.showWeekNumbers ? (
<CalendarWeekNumberCell week={week} weekIndex={index}>
{calendar.getWeekNumber(week)}
</CalendarWeekNumberCell>
) : null}
{week.map((day) => (
<CalendarTableCell
key={day.toString()}
tabIndex={tabIndex ?? undefined}
value={day}
>
{day.day}
</CalendarTableCell>
))}
</CalendarTableRow>
))}
</CalendarTableBody>
)}
</CalendarContext>
);
};
interface CalendarTableNextMonthProps
extends React.ComponentProps<typeof CalendarTableBody> {
/**
* The number of months to offset
*
* @default 1
*/
months?: number;
}
export const CalendarTableNextMonth = (props: CalendarTableNextMonthProps) => {
const { months = 1, tabIndex, ...rest } = props;
return (
<CalendarContext>
{(calendar) => {
const offset = calendar.getOffset({ months });
return (
<CalendarTableBody {...rest}>
{offset.weeks.map((week) => (
<CalendarTableRow
key={week.map((day) => day.toString()).join("-")}
>
{week.map((day) => (
<CalendarTableCell
key={day.toString()}
tabIndex={tabIndex ?? undefined}
value={day}
visibleRange={offset.visibleRange}
>
{day.day}
</CalendarTableCell>
))}
</CalendarTableRow>
))}
</CalendarTableBody>
);
}}
</CalendarContext>
);
};
export const CalendarTableHead = (
props: React.ComponentProps<typeof ArkCalendar.TableHead>
) => <ArkCalendar.TableHead data-slot="calendar-table-head" {...props} />;
export const CalendarTableRow = (
props: React.ComponentProps<typeof ArkCalendar.TableRow>
) => {
const { className, ...rest } = props;
return (
<ArkCalendar.TableRow
className={cn("mt-1 flex w-full", className)}
data-slot="calendar-table-row"
{...rest}
/>
);
};
export const CalendarTableHeader = (
props: React.ComponentProps<typeof ArkCalendar.TableHeader>
) => {
const { className, ...rest } = props;
return (
<ArkCalendar.TableHeader
className={cn(
"h-(--cell-size) w-full",
"flex items-center justify-center",
"select-none font-medium text-muted-foreground/64 text-xs",
"rounded-lg",
className
)}
data-slot="calendar-table-header"
{...rest}
/>
);
};
export const CalendarTableBody = (
props: React.ComponentProps<typeof ArkCalendar.TableBody>
) => <ArkCalendar.TableBody data-slot="calendar-table-body" {...props} />;
export const CalendarTableCell = (
props: React.ComponentProps<typeof ArkCalendar.TableCell>
) => {
const { value, visibleRange, className, ...rest } = props;
return (
<ArkCalendar.TableCell
className={cn(
"relative",
"h-(--cell-size) w-full",
"select-none text-center",
"[&:first-child[aria-selected=true]_div]:rounded-s-lg",
"[&:last-child[aria-selected=true]_div]:rounded-e-lg"
)}
data-slot="calendar-table-cell"
value={value}
visibleRange={visibleRange}
>
<ArkCalendar.TableCellTrigger
className={cn(
"inline-flex items-center justify-center gap-1",
"h-(--cell-size) w-full min-w-(--cell-size) data-[view=day]:h-(--cell-size)",
"select-none whitespace-nowrap font-normal text-base text-foreground leading-none sm:text-sm",
"rounded-lg border border-transparent",
"hover:bg-accent hover:text-accent-foreground",
"data-today:data-selected:after:bg-background data-today:after:absolute data-today:after:start-1/2 data-today:after:bottom-1 data-today:after:size-1 data-today:after:-translate-x-1/2 data-today:after:rounded-full data-today:after:bg-primary",
"data-focus:border-primary data-focus:bg-accent/30 data-focus:text-primary data-focus:ring-[3px] data-focus:ring-ring/32",
"outline-none focus-visible:border-primary focus-visible:ring-[3px] focus-visible:ring-ring/32",
"data-disabled:pointer-events-none data-disabled:opacity-64",
"data-unavailable:pointer-events-none data-unavailable:line-through data-unavailable:opacity-64",
"data-[view=day]:data-in-range:rounded-none data-[view=day]:data-in-range:not-[data-selected]:bg-primary/10",
"data-selected:bg-primary! data-selected:text-primary-foreground!",
"data-hover-range-start:rounded-s-lg! data-range-start:rounded-s-lg!",
"data-hover-range-end:rounded-e-lg! data-range-end:rounded-e-lg!",
"[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
className
)}
data-slot="calendar-table-cell-trigger"
{...rest}
/>
</ArkCalendar.TableCell>
);
};
export const CalendarWeekNumberHeaderCell = (
props: React.ComponentProps<typeof ArkCalendar.WeekNumberHeaderCell>
) => {
const { className, ...rest } = props;
return (
<ArkCalendar.WeekNumberHeaderCell
className={cn(
"h-(--cell-size) w-full min-w-(--cell-size)",
"flex items-center justify-center",
"select-none font-medium text-muted-foreground/64 text-xs",
className
)}
data-slot="calendar-week-number-header-cell"
{...rest}
/>
);
};
export const CalendarWeekNumberCell = (
props: React.ComponentProps<typeof ArkCalendar.WeekNumberCell>
) => {
const { className, ...rest } = props;
return (
<ArkCalendar.WeekNumberCell
className={cn(
"h-(--cell-size) w-full min-w-(--cell-size)",
"flex items-center justify-center",
"select-none text-muted-foreground text-xs",
className
)}
data-slot="calendar-week-number-cell"
{...rest}
/>
);
};
interface CalendarTableMonthsProps
extends React.ComponentProps<typeof CalendarTableBody> {
columns?: number;
format?: "short" | "long";
}
export const CalendarTableMonths = (props: CalendarTableMonthsProps) => {
const { columns = 4, format = "short", ...rest } = props;
return (
<CalendarContext>
{(calendar) => (
<CalendarTableBody {...rest}>
{calendar.getMonthsGrid({ columns, format }).map((months) => (
<CalendarTableRow
key={months.map((month) => month.value).join("-")}
>
{months.map((month) => (
<CalendarTableCell key={month.value} value={month.value}>
{month.label}
</CalendarTableCell>
))}
</CalendarTableRow>
))}
</CalendarTableBody>
)}
</CalendarContext>
);
};
interface CalendarTableYearsProps
extends React.ComponentProps<typeof CalendarTableBody> {
columns?: number;
}
export const CalendarTableYears = (props: CalendarTableYearsProps) => {
const { columns = 4, ...rest } = props;
return (
<CalendarContext>
{(calendar) => (
<CalendarTableBody {...rest}>
{calendar.getYearsGrid({ columns }).map((years) => (
<CalendarTableRow key={years.map((year) => year.value).join("-")}>
{years.map((year) => (
<CalendarTableCell key={year.value} value={year.value}>
{year.label}
</CalendarTableCell>
))}
</CalendarTableRow>
))}
</CalendarTableBody>
)}
</CalendarContext>
);
};Update the import paths to match your project setup.
Anatomy
Calendar
├── CalendarClearTrigger
├── CalendarContent
├── CalendarControl
├── CalendarInput
├── CalendarLabel
├── CalendarMonthSelect
├── CalendarNextTrigger
├── CalendarPositioner
├── CalendarPresetTrigger
├── CalendarPrevTrigger
├── CalendarViewDate
├── CalendarTable
│ ├── CalendarTableBody
│ ├── CalendarTableCell
│ │ └── CalendarTableCellTrigger
│ ├── CalendarTableHead
│ ├── CalendarTableHeader
│ └── CalendarTableRow
├── CalendarTrigger
├── CalendarView
│ ├── CalendarViewControl
│ └── CalendarViewTrigger
└── CalendarYearSelectUsage
import {
Calendar,
CalendarNextTrigger,
CalendarPrevTrigger,
CalendarTable,
CalendarTableDays,
CalendarViewControl,
CalendarViewDate,
CalendarWeekDays,
} from "@/components/ui/calendar";
import { Card, CardContent } from "@/components/ui/card";<Card className="[--space:--spacing(2)]">
<CardContent>
<Calendar>
<CalendarViewControl>
<CalendarPrevTrigger />
<CalendarViewDate />
<CalendarNextTrigger />
</CalendarViewControl>
<CalendarTable>
<CalendarWeekDays />
<CalendarTableDays />
</CalendarTable>
</Calendar>
</CardContent>
</Card>Accessibility
| Key | Description |
|---|---|
Enter | Activate the focused control. |
Space | Activate the focused control. |
Demos
Booked Dates
Controlled
Custom Cell Size
Default Value
Default View
Fixed Weeks
Locale
Max Selected
Min Max
Month Picker
Month Range
Month Year Selector
Multiple
Multiple Months
Presets
Range
Root Provider
Select Today
Week Numbers
Year Picker
Year Range
API Reference
CalendarClearTrigger
button
PropType
AttributeType
CalendarControl
div
PropType
AttributeType
CalendarLabel
label
PropType
AttributeType
CalendarMonthSelect
select
PropType
AttributeType
CalendarNextTrigger
button
PropType
AttributeType
CalendarPresetTrigger
button
PropType
AttributeType
CalendarPrevTrigger
button
PropType
AttributeType
CalendarViewDate
div
PropType
AttributeType
Calendar
div
PropType
AttributeType
CalendarTable
table
PropType
AttributeType
CalendarTableBody
tbody
PropType
AttributeType
CalendarTableCell
td
PropType
AttributeType
CSS variableType
CalendarTableCellTrigger
div
PropType
AttributeType
CSS variableType
CalendarTableHead
thead
PropType
AttributeType
CalendarTableHeader
th
PropType
AttributeType
CSS variableType
CalendarTableRow
tr
PropType
AttributeType
CalendarTrigger
button
PropType
AttributeType
CalendarView
div
PropType
AttributeType
CalendarViewControl
div
PropType
AttributeType
CalendarViewTrigger
button
PropType
AttributeType
CalendarYearSelect
select
PropType
AttributeType
CalendarTodayTrigger
PropType
AttributeType
CalendarWeekDays
PropType
AttributeType
CalendarTableDays
PropType
AttributeType
CalendarTableNextMonth
PropType
AttributeType
CalendarWeekNumberHeaderCell
th
PropType
AttributeType
CSS variableType
CalendarWeekNumberCell
td
PropType
AttributeType
CSS variableType
CalendarTableMonths
PropType
AttributeType
CalendarTableYears
PropType
AttributeType
CalendarRootProvider
div
PropType
AttributeType