Calendar
A date grid for single, range and multiple selection, with sliding months, range previews and touch-sized days.
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
pnpm dlx shadcn@latest add https://hextaui.com/r/calendar.jsonAdds the component, the HextaUI theme tokens and any HextaUI components it depends on.
Add the theme tokens to your global CSS, if you haven’t yet.
Install the dependencies.
pnpm add react-day-picker @base-ui/react @tabler/icons-react class-variance-authority cnCopy and paste the following code into your project.
components/ui/calendar.tsx components/ui/button.tsx lib/motion.ts Update the import paths to match your project setup.
Calendar wraps react-day-picker’s <DayPicker />, so every DayPicker prop works as documented on daypicker.dev. HextaUI adds styling, month transitions, a range preview and a hydration-safe today.
With mode="range", after the first click hovering or focusing a day previews the range the next click will select. numberOfMonths shows months side by side, stacked on narrow screens.
min and max limit the range length in days. With excludeDisabled, a range that would include a disabled day starts over instead.
mode="multiple" toggles individual days. max caps how many can be selected.
captionLayout="dropdown" replaces the caption with native selects, so phones get their own picker. Set startMonth and endMonth to bound the years.
Navigation stops at startMonth and endMonth, and disabled blocks days outside the window. useToday() gives a today that is safe to use during server rendering.
Pass month and onMonthChange to drive the visible month. Jumps slide in the direction of travel and the height eases between 5 and 6 week rows.
showWeekNumber adds a week column. ISOWeek uses ISO numbering, starting on Monday. showOutsideDays={false} hides days of other months.
Pass today to pin the highlighted day, for tests or another time zone. animate={false} turns off month transitions.
Inside a sheet, popover or dialog the calendar drops its own background and blends with the surface.
Pass a locale from react-day-picker/locale and dir="rtl". Arrows, navigation and the slide direction all mirror. Inside a dir="rtl" DirectionProvider, the direction is picked up automatically.
Focus a day, then use these keys. Moving past the visible month changes the month.
| Key | Action |
|---|---|
| ←→ | Previous or next day. Reversed in right-to-left layouts. |
| ↑↓ | Same day of the previous or next week. |
| Shift←→ | Previous or next month. |
| Shift↑↓ | Previous or next year. |
| Page UpPage Down | Previous or next month. |
| ShiftPage UpPage Down | Previous or next year. |
| Home | First day of the week. |
| End | Last day of the week. |
| EnterSpace | Selects the focused day. |
- The month is a grid. Each day is a button with a full date label, and selected days set
aria-selected. - Month changes made with the keyboard skip the slide and only fade, so focus never moves under a moving grid.
- With reduced motion, month changes fade and the height change is instant.
- On touch screens the day cells grow to 44px.
Accepts every <DayPicker /> prop. The defaults below differ from DayPicker’s or are added by HextaUI.
| Prop | Type | Default |
|---|---|---|
modeWithout a mode, days are not selectable. | "single" | "multiple" | "range" | – |
selectedMatches the mode. | Date | Date[] | DateRange | – |
onSelect | (selected, triggerDate, modifiers, event) => void | – |
requiredPrevents deselecting the last selection. | boolean | – |
minMinimum days in a range, or selected in multiple mode. | number | – |
maxMaximum days in a range, or selected in multiple mode. | number | – |
excludeDisabledRange mode. | boolean | – |
disabled | Matcher | Matcher[] | – |
monthControlled month. | Date | – |
defaultMonth | Date | – |
onMonthChange | (month: Date) => void | – |
startMonth | Date | – |
endMonth | Date | – |
numberOfMonths | number | 1 |
captionLayout | "label" | "dropdown" | "dropdown-months" | "dropdown-years" | "label" |
navLayoutHextaUI default. Arrows sit on either side of the caption. | "around" | "after" | "around" |
showOutsideDaysHextaUI default. | boolean | true |
animateMonth slide and height transitions. HextaUI default. | boolean | true |
buttonVariantVariant of the previous and next buttons. | Button variant | "ghost" |
showWeekNumber | boolean | false |
ISOWeek | boolean | false |
weekStartsOn | 0 | 1 | 2 | 3 | 4 | 5 | 6 | – |
fixedWeeks | boolean | false |
todayDefaults to the client’s today, kept in sync across midnight and hydration. | Date | – |
timeZone | string | – |
locale | Partial<DayPickerLocale> | – |
dir | "ltr" | "rtl" | – |
footer | ReactNode | – |
| Attribute | Description |
|---|---|
data-slot="calendar" | Target the calendar root in CSS. |
--cell-size | Day cell size. 36px, or 44px on touch screens. |
--cell-radius | Corner radius of day cells and buttons. |
data-slot="calendar-day" | Day cells. Carry data-selected, data-disabled, data-outside, data-today, data-hidden and data-focused. |
data-preview | On day cells: start, middle or end of the hovered range preview. |
data-range-middle | On day cells inside a selected range. |
The button inside each day. Pass your own to components={{ DayButton }} and reuse this one to keep the styling.
| Attribute | Description |
|---|---|
data-slot="calendar-day-button" | Target day buttons in CSS. |
data-day | The ISO date, like 2026-10-03. |
data-today | Present on today, outside days excluded. |
data-selected-single | Selected outside a range. |
data-range-start | First day of the range. |
data-range-middle | A day inside the range. |
data-range-end | Last day of the range. |
Returns today as a Date on the client and undefined during server rendering, so bounds built from it never cause a hydration mismatch. It updates at midnight. See the useToday guide.
- ButtonButtons in every variant and size, with a built-in loading, success and error flow that skips the spinner for fast requests.
- MotionThe easing curves, durations and reduced-motion check every component animates with, plus hooks for size morphs and sliding highlights.
- useTodayToday's date that rolls over at midnight and when the tab comes back, without a hydration mismatch.
- CheckboxA checkbox whose check draws in, with indeterminate parents, groups and labels that share its hover.
- ComboboxA filterable select with chips, groups and async results, in a popup that resizes as you type.
- Date pickerA button that opens a calendar in a popover, or a bottom sheet on phones, for single dates and ranges.