CalendarPreview

One subcomposed date component that owns date state and popover state explicitly.
1<CalendarPreview defaultMonth={new Date(2024, 3, 1)}>
2 <CalendarPreview.Nav />
3 <CalendarPreview.Grid />
4</CalendarPreview>

CalendarPreview replaces Calendar, DatePicker and RangePicker with a single root and dot-notation parts. Every piece of state is owned explicitly — selection, visible month, open, granularity — so nothing is private and no part needs to reach around another.

It ships alongside the current calendar family; those exports are removed a release after this one is documented.

Anatomy

1import { CalendarPreview } from '@raystack/apsara'
2
3<CalendarPreview>
4 <CalendarPreview.Trigger>
5 <CalendarPreview.Input />
6 </CalendarPreview.Trigger>
7 <CalendarPreview.Content initialFocus={false}>
8 <CalendarPreview.GranularityTabs />
9 <CalendarPreview.Nav />
10 <CalendarPreview.Grid />
11 <CalendarPreview.MonthGrid />
12 <CalendarPreview.Footer>
13 <CalendarPreview.Cancel />
14 <CalendarPreview.Apply />
15 </CalendarPreview.Footer>
16 </CalendarPreview.Content>
17</CalendarPreview>

Drop any part you do not need. Grid renders for the day granularity and MonthGrid for the rest, so a picker offering both keeps both in the tree.

API Reference

Root

Owns every piece of state and provides it to the parts.

Prop

Type

Trigger

Anchors the popover. Renders a div, never a <button>, because it may contain a typed input.

Prop

Type

Content

The portaled surface. Positioning props are passed here directly.

Prop

Type

Input

The typed single-date field.

Prop

Type

RangeInput

Paired start and end fields. Both are typable.

Prop

Type

Caption plus previous and next buttons. Renders for the day granularity only — the other granularities scroll rather than page.

Prop

Type

Grid

The day grid.

Prop

Type

MonthGrid

Month, quarter, half-year and year selection, as a scrolling list of years.

Prop

Type

GranularityTabs

Day, Month, Quarter, Half-year and Year. Renders only when the root offers more than one granularity.

Prop

Type

Action row for Apply and Cancel.

Prop

Type

Examples

State

Open, visible month, and bounds are all ordinary props. The visible month is independent of the value but initialises from it, so a picker holding a date in another year opens on that year rather than today.

1<CalendarPreview defaultOpen onOpenChange={(open) => console.log(open)}>
2 <CalendarPreview.Trigger>
3 <CalendarPreview.Input />
4 </CalendarPreview.Trigger>
5 <CalendarPreview.Content initialFocus={false}>
6 <CalendarPreview.Nav />
7 <CalendarPreview.Grid />
8 </CalendarPreview.Content>
9</CalendarPreview>

Granularity

granularities lists what the user may switch between; the tabs appear only when there is more than one.

1<CalendarPreview
2 defaultMonth={new Date(2024, 3, 1)}
3 granularities={["day", "month", "quarter", "half-year", "year"]}
4>
5 <CalendarPreview.GranularityTabs />
6 <CalendarPreview.Nav />
7 <CalendarPreview.Grid />
8 <CalendarPreview.MonthGrid />
9</CalendarPreview>

MonthGrid emits the first day of the chosen period — a quarter pick in 2024 Q3 yields 1 July 2024.

Commit and locking

commit="explicit" buffers edits until Apply, so a popover can be abandoned without the parent seeing intermediate states. lock holds one endpoint of a range read-only while the other stays pickable.

1<CalendarPreview
2 commit="explicit"
3 defaultMonth={new Date(2024, 3, 1)}
4 defaultOpen
5>
6 <CalendarPreview.Trigger>
7 <CalendarPreview.Input />
8 </CalendarPreview.Trigger>
9 <CalendarPreview.Content initialFocus={false}>
10 <CalendarPreview.Nav />
11 <CalendarPreview.Grid />
12 <CalendarPreview.Footer>
13 <CalendarPreview.Cancel />
14 <CalendarPreview.Apply />
15 </CalendarPreview.Footer>

Inside a Field

Input reads field context, so the label association, required and aria-invalid all wire up by composition. The component renders no error text itself — report through onValidityChange and let Field.Error present it.

1<Field>
2 <Field.Label>Starts</Field.Label>
3 <CalendarPreview>
4 <CalendarPreview.Trigger>
5 <CalendarPreview.Input />
6 </CalendarPreview.Trigger>
7 <CalendarPreview.Content initialFocus={false}>
8 <CalendarPreview.Nav />
9 <CalendarPreview.Grid />
10 </CalendarPreview.Content>
11 </CalendarPreview>
12 <Field.Error />
13</Field>

Accessibility

  • The day grid is react-day-picker's, which supplies the grid roles, roving tabindex and arrow-key navigation.
  • Trigger renders a non-button element with button semantics supplied by Base UI: it carries role, tabindex, aria-haspopup, aria-expanded, and aria-disabled rather than a disabled attribute.
  • Pass initialFocus={false} to Content whenever the trigger contains a typed field. Without it the popup takes focus on open and keystrokes never reach the field.
  • The Nav caption is an aria-live="polite" region, so changing month is announced.
  • MonthGrid cells are buttons with aria-pressed, not tabs — the design reuses the standalone tab visual, but tab semantics without tabpanels would be wrong.
  • readOnly leaves days legible and focusable while refusing edits; disabled removes them from interaction and prevents the popover opening at all.