Calendar renders one month as a table of day buttons. It is the grid DatePicker opens in a popover, documented separately because you can place it inline, and it carries almost none of its own API: one PDS prop, and react-aria’s calendar underneath it.
When to use
Section titled “When to use”Calendar has no field, so a date gets picked out of the grid or not at all, and the grid is a fixed 266px table that does not reflow. The question is whether a month of squares fits the layout you have: an inline birthday or effective-date field, yes; a filter bar or a table cell, no.
Where the reader should be able to type the date as well as pick it, the component is DatePicker, which is this grid in a popover behind a date field. DatePicker exists in PDS and is not documented here yet. For two dates and the span between them, there is Range Calendar.
Selection
Section titled “Selection”Days that fall outside the visible month are not drawn at all. The cell is
rendered hidden rather than grayed, so a month can begin with empty squares
and there is no option to show the neighboring month’s dates in them.
Focus today rather than selecting it
Section titled “Focus today rather than selecting it”Calendar draws no marker on today. The cell styles selected, disabled and
invalid dates, and nothing else, so today reads as today only while it is the
focused cell. defaultFocusedValue puts the keyboard there and leaves value
empty, which means the reader still has to press it.
Keep it that way. Today is a fact about the calendar and the selection is an answer, so a prefilled date records an answer nobody gave. On a date of birth field that answer is wrong every day of the year but one.
Say in words which dates cannot be picked
Section titled “Say in words which dates cannot be picked”minValue and maxValue push everything past the bound into the disabled
state, which the cell renders as a lighter number with nothing else changed.
isDateUnavailable gets less than that: the PDS cell reads isDisabled and
isInvalid and never reads isUnavailable, so a date ruled out one at a time
looks exactly like a date you can choose, stays focusable, and then refuses the
press. React-aria’s own API asks the consumer for a visual affordance there, and
PDS does not draw one.
So the grid cannot explain itself, and the reader who finds out from the grid alone is the one who has already clicked. Put the constraint in text beside the calendar, and name it rather than gesturing at it: “Weekends are unavailable”, not “some dates are unavailable”.
Accessibility
Section titled “Accessibility”Arrow keys move by a day, and up and down by a week
Section titled “Arrow keys move by a day, and up and down by a week”The month is a role="grid" table and Tab reaches it once. Inside it, left and
right move by a day, up and down move by a week, and moving past the edge of the
month brings the next one into view. The two navigation buttons are named by
react-aria in the reader’s locale, so there is no aria-label for you to set on
them.
Selection changes are already announced
Section titled “Selection changes are already announced”useCalendarBase writes the new value into a live region, and announces the new
month when Previous or Next is pressed from outside the grid. That is worth knowing mainly because it is losable: a
month grid assembled by hand out of buttons tells a sighted reader the selection
changed and tells nobody else.
This is the ARIA grid pattern as react-aria implements it. Whether the PDS build honors all of it has not been verified here, and this paragraph should be replaced by someone who has checked.
Content
Section titled “Content”Spell the month out wherever you display the date
Section titled “Spell the month out wherever you display the date”The grid itself is formatted for the reader’s locale, so the rule is about
everywhere the chosen date is shown afterwards: the field, the summary line, and
any table it ends up in. 03/04 is the fourth of March to one reader and the
third of April to another, and nothing around it settles which. Mar 4, 2025 is
one date to both.
Wording for the field and its helper text is on Writing.
calendarRef is the only prop Calendar declares. The value, the change handler,
the bounds and which dates are unavailable all come from
AriaCalendarProps<DateValue>, which Calendar hands to useCalendarState
untouched, so the table below is one row and the API is roughly twenty. The
locale is not among them: Calendar reads it from react-aria’s context and
overrides anything passed in.
| Name | Type |
|---|---|
calendarRef |
|
AriaCalendarProps<DateValue>.Was this page helpful?