Calendar
Month grid for picking a single date, built on React Aria, with keyboard navigation and month paging.
import { CalendarDate } from "@internationalized/date"
import { Calendar } from "@pockets-ui/components"Dates are @internationalized/date values, not JavaScript Dates — a
CalendarDate has no time and no time zone, so it can't shift a day when it
crosses one. Note its month is 1-based: new CalendarDate(2026, 7, 15) is
15 July 2026.
Anatomy
| Part | Role |
|---|---|
Calendar.Root | Owns the value, bounds, and availability rules. |
Calendar.ActionRow | The header strip above the grid. |
Calendar.ActionButton | Month paging. slot="previous" / slot="next". |
Calendar.Heading | The current month and year. |
Calendar.Grid | The grid itself. |
Calendar.Header | Weekday header row — a render function per day. |
Calendar.HeaderCell | One weekday label. |
Calendar.Body | Day cells — a render function per date. |
Calendar.Cell | One day. |
Basic
The full composition, spelled out. The examples below reuse it as a
DemoCalendar wrapper so each one shows only the prop that's varying.
October 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
27 | 28 | 29 | 30 | 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 | 31 |
Code
import { Calendar } from "@pockets-ui/components"
import { ChevronLeftIcon, ChevronRightIcon } from "./shared"
export default function CalendarBasic() {
return (
<Calendar.Root aria-label="Event date">
<Calendar.ActionRow>
<Calendar.ActionButton slot="previous">
<ChevronLeftIcon />
</Calendar.ActionButton>
<Calendar.Heading />
<Calendar.ActionButton slot="next">
<ChevronRightIcon />
</Calendar.ActionButton>
</Calendar.ActionRow>
<Calendar.Grid>
<Calendar.Header>
{(day) => <Calendar.HeaderCell>{day}</Calendar.HeaderCell>}
</Calendar.Header>
<Calendar.Body>{(date) => <Calendar.Cell date={date} />}</Calendar.Body>
</Calendar.Grid>
</Calendar.Root>
)
}Default value
July 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
28 | 29 | 30 | 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 | 31 | 1 |
Code
import { CalendarDate } from "@internationalized/date"
import { DemoCalendar } from "./shared"
export default function CalendarDefaultValue() {
return (
<DemoCalendar
aria-label="Appointment date"
defaultValue={new CalendarDate(2026, 7, 15)}
/>
)
}Min and max dates
Dates outside minValue/maxValue can't be selected or focused.
defaultFocusedValue decides which month opens without selecting anything.
July 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
28 | 29 | 30 | 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 | 31 | 1 |
Code
import { CalendarDate } from "@internationalized/date"
import { DemoCalendar } from "./shared"
export default function CalendarMinMax() {
return (
<DemoCalendar
aria-label="Booking date"
defaultFocusedValue={new CalendarDate(2026, 7, 15)}
minValue={new CalendarDate(2026, 7, 6)}
maxValue={new CalendarDate(2026, 7, 24)}
/>
)
}Unavailable dates
isDateUnavailable blocks scattered dates inside an otherwise valid range —
booked days, holidays, blackout dates — while keeping them visible.
July 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
28 | 29 | 30 | 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 | 31 | 1 |
Code
import { CalendarDate } from "@internationalized/date"
import { DemoCalendar } from "./shared"
export default function CalendarUnavailableDates() {
return (
<DemoCalendar
aria-label="Delivery date"
defaultFocusedValue={new CalendarDate(2026, 7, 15)}
isDateUnavailable={(date) => date.day === 13 || date.day === 21}
/>
)
}Disabled
October 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
27 | 28 | 29 | 30 | 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 | 31 |
Code
import { DemoCalendar } from "./shared"
export default function CalendarDisabled() {
return <DemoCalendar aria-label="Unavailable calendar" isDisabled />
}Related
- DatePicker — this grid in a popover, with a date field.
- DateField — the segmented text input on its own.