Calendar

A full month or year surface for displaying and selecting dates, with a cell renderer for putting your own content in a day.

Usage

When to reach for a month grid instead of a date field, which of the two sizes to use, and how to put content in a day.

When to use

Calendar is an always-visible month or year grid for showing data against dates: bookings, deadlines, shifts, a heat map. If the answer belongs in a form field, use something else.
  • A month of something the reader scans, like rooms sold per night, where the shape of the month is the information.
  • A year grid, with mode="year", when the reader compares months rather than days.
Use something else when:
  • Use DatePicker in a form. A field with a popup, one value, a range variant and validation is DatePicker.
  • The date is one column among several the reader sorts and filters. That is a Table.
  • The order of events matters more than the gaps between them. That is a Timeline.
  • Each day needs a time axis. A month grid has no room for hours.

The full form and the card form

Set fullscreen={false} inside a card. The default full-size calendar wants a page to itself. The compact one fills the width you give it.
  • Put content in the full form, which gives every date a content slot of fixed height.
  • That slot scrolls, so a day with eight events becomes a scroll area inside a cell. Show two or three and a count.

Month against year

mode takes month (the default) or year. It picks the grid, not the value, which is one date in both.
  • month shows six weeks, so the first and last rows carry days from the months around it.
  • year shows twelve months. cellRender still runs, with info.type set to "month".
  • Pass mode and you control it, so set it back from onPanelChange.
  • Keep one mode per view. A grid that changes what a cell means is a different view.

Putting content in a day

cellRender adds, fullCellRender replaces. cellRender content goes below the date the cell already shows, so return null for cells you skip. Returning info.originNode draws the date twice. fullCellRender replaces the whole cell, so draw the date yourself and return info.originNode for cells you skip.
  • Give every compact cell the same height. In a compact calendar a cell grows with what cellRender returns, so dates with content sit higher than dates without. Return an element of fixed height for every date, hidden on empty days.
  • The old cell props are deprecated. dateCellRender, dateFullCellRender, monthCellRender and monthFullCellRender still work and warn. Use cellRender and fullCellRender, and check info.type for "date" or "month".
Add arrows with headerRender. The default header has year and month selects but no previous and next buttons. headerRender receives { value, type, onChange, onTypeChange } and replaces the whole toolbar.
  • Call onChange with a new date to move the panel, and onTypeChange to switch between the month and year grids.
  • A move through onChange reaches onSelect with source: "customize".
  • Label the controls you add. The Accessibility tab has the details.

Limiting the dates

validRange and disabledDate work together. validRange stops navigation outside a range. disabledDate keeps every month reachable and disables the matching days. A booking view often wants both: the range for the season, the function for weekends.
  • A date outside validRange is disabled whatever disabledDate returns.
  • A disabled day blocks the click and still renders its cellRender content, so it can show why it is closed.

Controlling it, and the date library it expects

defaultValue sets the starting date and falls back to today. value controls both the selection and the month shown.
  • onSelect fires for every change. It fires for a click on a day and for a year or month picked in the header, and info.source says which. onPanelChange fires when the month shown or the mode changes.
  • onChange skips a click on the day that is already selected. onSelect fires either way.
  • Every date in and out is a dayjs object. Convert a Date or an ISO string on the way in.
  • Read the date type off the component. If dayjs is not in your own dependencies, importing it fails under pnpm. Derive the type from ComponentProps<typeof Calendar> instead.
  • For another date library, use antd's Calendar.generateCalendar(config) and wrap the result yourself.