Skip to main content

calendar

A date field component that allows users to enter and edit date.

Philosophy

Calendars are complex accessibility challenges disguised as simple grids. We own the entire calendar stack - from the headless engine (@gentleduck/calendar) to the compound primitives (@gentleduck/primitives/calendar) to this styled component. The adapter pattern lets you swap date libraries, the hook layer handles state management, and this component applies Tailwind styling via data-* attribute selectors.

How It's Built

Loading diagram...

Installation


npx @gentleduck/cli add calendar

npx @gentleduck/cli add calendar

Usage

import { Calendar } from "@/components/ui/calendar"
import { Calendar } from "@/components/ui/calendar"
const [date, setDate] = React.useState<Date | undefined>(new Date())
 
return (
  <Calendar
    mode="single"
    selected={date}
    onSelect={setDate}
    className="rounded-lg border"
  />
)
const [date, setDate] = React.useState<Date | undefined>(new Date())
 
return (
  <Calendar
    mode="single"
    selected={date}
    onSelect={setDate}
    className="rounded-lg border"
  />
)

See the @gentleduck/calendar package docs for more information on the headless engine, date adapters, and advanced features.

Examples

Date Picker (Popover)

Date Picker (Input)

Date Input (Natural Language)

Form Integration

Multi-Month Range Picker

Range Calendar

Date Constraints

Multi-Select

Event Calendar

Custom Cell Size

Click to View Events (Popover)

Booked Dates (Strikethrough)

Booking Calendar

Multi-Range Selection

Multi-Range with Shift+Click

Presets

Persian (Jalali) Calendar

Islamic (Hijri) Calendar

Hebrew Calendar

Hebrew Calendar (English)

Islamic Calendar (English)

Persian Calendar (English)

Notes

Blocks

We have built a collection of 30+ calendar blocks that you can use to build your own calendar components.

See all calendar blocks in the Blocks Library page.

Migrated from react-day-picker

Date Picker

You can use the <Calendar> component to build a date picker. See the Date Picker page for more information.

RTL Support

Direction is resolved through the shared primitives direction module. Use a local dir="rtl" override when the component exposes it, or set DirectionProvider at app/root level for global RTL/LTR behavior.

Motion

Use MotionCalendar for smooth month navigation transitions and staggered day cell entry powered by motion. The grid slides directionally when navigating months, and day cells fade in with a stagger effect.

API Reference

Calendar

PropTypeDefaultDescription
adapterAdapter.IDateAdapter<Date>NativeAdapterDate adapter for alternative calendar systems (Islamic, Persian, Hebrew)
mode'single' | 'range' | 'multi' | 'multi-range''single'Selection mode
selectedDate | DateRange<Date> | Date[] | null-Controlled selection value
onSelect(value) => void-Called when the selection changes
disabledDate[] | ((date: Date) => boolean)-Dates that cannot be selected
defaultMonthDate-Default month to display (uncontrolled)
monthDate-Controlled displayed month
onMonthChange(month: Date) => void-Called when the displayed month changes
showOutsideDaysbooleantrueShow days from adjacent months
fixedWeeksbooleanfalseAlways show 6 weeks
numberOfMonthsnumber1How many months to show side by side
showDropdownsbooleantrueShow month/year dropdown selectors
yearRange{ from: number; to: number }{ from: now-100, to: now+10 }Range of years in the year dropdown
localestring-BCP 47 locale tag (e.g. 'ar-SA', 'ja-JP')
dir'ltr' | 'rtl'-Text direction override
fromDateDate-Earliest selectable date
toDateDate-Latest selectable date
buttonVariantstring'ghost'Variant style for navigation buttons
onDismiss() => void-Called when the user presses Escape
classNamestring-Additional CSS classes for the root container
renderDay(day: Grid.ICalendarDay<Date>, children: ReactNode) => ReactNode-Custom render function for day cell content
renderHeader(context: CalendarHeaderContext) => ReactNode-Custom render function for the navigation header
renderWeekday(day: string, index: number) => ReactNode-Custom render function for weekday column labels
renderFooter(months: Grid.ICalendarMonth<Date>[]) => ReactNode-Render content below the calendar grid

Render Props

The Calendar component exposes render props that let you customize every visual part without forking the component.

renderDay

Customize the content inside each day cell. Receives the Grid.ICalendarDay object and the default children (the date number).

<Calendar
  renderDay={(day, children) => (
    <>
      {children}
      {hasEvents(day.date) && (
        <span className="size-1 rounded-full bg-primary" />
      )}
    </>
  )}
/>
<Calendar
  renderDay={(day, children) => (
    <>
      {children}
      {hasEvents(day.date) && (
        <span className="size-1 rounded-full bg-primary" />
      )}
    </>
  )}
/>

The day object exposes: date, isToday, isSelected, isDisabled, isOutside, isHidden, isWeekend, isRangeStart, isRangeEnd, isRangeMiddle.

renderHeader

Replace the entire navigation header. Receives a context object with navigation controls.

<Calendar
  renderHeader={({ month, title, direction, goToPrevMonth, goToNextMonth, isPrevDisabled, isNextDisabled }) => (
    <div className="flex items-center justify-between px-2">
      <button onClick={goToPrevMonth} disabled={isPrevDisabled}><-</button>
      <span className="font-semibold">{title}</span>
      <button onClick={goToNextMonth} disabled={isNextDisabled}>-></button>
    </div>
  )}
/>
<Calendar
  renderHeader={({ month, title, direction, goToPrevMonth, goToNextMonth, isPrevDisabled, isNextDisabled }) => (
    <div className="flex items-center justify-between px-2">
      <button onClick={goToPrevMonth} disabled={isPrevDisabled}><-</button>
      <span className="font-semibold">{title}</span>
      <button onClick={goToNextMonth} disabled={isNextDisabled}>-></button>
    </div>
  )}
/>

renderWeekday

Customize individual weekday column headers. Receives the abbreviation and column index.

<Calendar
  renderWeekday={(day, index) => (
    <span className={index === 0 || index === 6 ? "text-red-400" : ""}>
      {day}
    </span>
  )}
/>
<Calendar
  renderWeekday={(day, index) => (
    <span className={index === 0 || index === 6 ? "text-red-400" : ""}>
      {day}
    </span>
  )}
/>

renderFooter

Add content below the calendar grid. Receives the current months array for context.

<Calendar
  renderFooter={() => (
    <div className="mt-2 text-xs text-muted-foreground">
      Pick a date to continue
    </div>
  )}
/>
<Calendar
  renderFooter={() => (
    <div className="mt-2 text-xs text-muted-foreground">
      Pick a date to continue
    </div>
  )}
/>

Data Attributes

The calendar uses data-* attributes on day cells for styling. Target these in your CSS:

AttributeWhen Present
data-selected="true"Day is selected
data-selected-single="true"Day is selected (single mode, not part of range)
data-today="true"Day is today
data-focused="true"Day has keyboard focus
data-range-start="true"Day is the start of a range
data-range-end="true"Day is the end of a range
data-range-middle="true"Day is between range start and end
data-dayFormatted date string for the day (always present)

MotionCalendar

Adds directional slide transitions on month navigation (with blur) and staggered day cell entry animation. Uses LazyMotion for a lightweight bundle (~5KB). Requires the motion package.

PropTypeDefaultDescription
...propsCalendarProps-All props from Calendar are supported

CSS Variables

VariableDefaultDescription
--gentleduck-calendar-cell--spacing(8) (32px)Size of day cells and nav buttons