Skip to content
UI

Calendar

A month grid for picking a single date or a date range.

@zenoxai/ui/components/calendar Markdown
October 2026
function CalendarDemo() {
  const [date, setDate] = React.useState<Date | undefined>(
    new Date(2026, 9, 14)
  )

  return (
    <Calendar
      mode="single"
      selected={date}
      onSelect={setDate}
      defaultMonth={date}
      className="rounded-lg border border-border"
    />
  )
}

Usage

import * as React from "react"

import { Calendar } from "@zenoxai/ui/components/calendar"

export function ScanDate() {
  const [date, setDate] = React.useState<Date | undefined>()

  return <Calendar mode="single" selected={date} onSelect={setDate} />
}

Calendar is React DayPicker styled with the Zenox tokens. It accepts every DayPicker prop: mode, selected, onSelect, disabled, numberOfMonths, locale and the rest. The wrapper adds buttonVariant, the Button variant of the previous and next month buttons (ghost by default).

The selected day, and the start and end of a range, are filled with the primary color and keep it on hover. The days inside a range sit on the soft selection band (primary-soft), hover is the plain surface-muted step, and today is marked by its text (primary, semibold) rather than a fill, so the three never look alike.

Render the calendar inline when picking a date is the main task of the view, such as scheduling a scan. In a form, open it from a button in a Popover, so it takes space only while the user picks. For a date the user knows by heart, like a birth date, a typed Input is faster.

Examples

Date range

mode="range" selects a start and an end date. selected is a { from, to } object; to is undefined until the user picks the second date. Show two months with numberOfMonths={2} so most ranges fit without paging.

October 2026
November 2026
function CalendarRange() {
  const [range, setRange] = React.useState<
    { from: Date | undefined; to?: Date } | undefined
  >({ from: new Date(2026, 9, 6), to: new Date(2026, 9, 19) })

  return (
    <Calendar
      mode="range"
      selected={range}
      onSelect={setRange}
      defaultMonth={range?.from}
      numberOfMonths={2}
      className="rounded-lg border border-border"
    />
  )
}

Disabled days

disabled takes one or more matchers: a date, a { before } or { after } bound, a { from, to } range, a { dayOfWeek } list or a function. Here a scan can only be scheduled on a weekday from October 12 on.

October 2026
function CalendarDisabledDays() {
  const [date, setDate] = React.useState<Date | undefined>()

  return (
    <Calendar
      mode="single"
      selected={date}
      onSelect={setDate}
      defaultMonth={new Date(2026, 9, 1)}
      disabled={[{ before: new Date(2026, 9, 12) }, { dayOfWeek: [0, 6] }]}
      className="rounded-lg border border-border"
    />
  )
}

Month and year dropdowns

captionLayout="dropdown" replaces the caption with month and year selects, for dates far from today. Bound them with startMonth and endMonth.

March 2026
function CalendarDropdowns() {
  const [date, setDate] = React.useState<Date | undefined>(new Date(2026, 2, 3))

  return (
    <Calendar
      mode="single"
      selected={date}
      onSelect={setDate}
      defaultMonth={date}
      captionLayout="dropdown"
      startMonth={new Date(2020, 0)}
      endMonth={new Date(2026, 11)}
      className="rounded-lg border border-border"
    />
  )
}

Date picker

A button that shows the chosen date and opens the calendar in a popover. Control the popover with open and onOpenChange to close it once a date is picked.

function CalendarDatePicker() {
  const [open, setOpen] = React.useState(false)
  const [date, setDate] = React.useState<Date | undefined>()

  return (
    <Popover open={open} onOpenChange={setOpen}>
      <PopoverTrigger
        render={<Button variant="outline" className="w-52 justify-start" />}
      >
        <CalendarIcon data-icon="inline-start" />
        {date
          ? date.toLocaleDateString("en-US", { dateStyle: "medium" })
          : "Pick a report date"}
      </PopoverTrigger>
      <PopoverContent align="start" className="w-auto p-0">
        <Calendar
          mode="single"
          selected={date}
          onSelect={(next) => {
            setDate(next)
            setOpen(false)
          }}
          defaultMonth={date ?? new Date(2026, 9, 1)}
        />
      </PopoverContent>
    </Popover>
  )
}

Accessibility

  • The month is a grid. Arrow keys move between days, Page Up and Page Down change the month (with Shift, the year), Home and End jump to the start and end of the week, and Enter or Space selects the focused day.
  • Every day button has a full date as its label (“Wednesday, October 14th, 2026”), and the previous and next buttons are labelled. Pass locale (from react-day-picker/locale) to translate the labels together with the month and weekday names.
  • Disabled days stay in the grid but cannot be selected. Explain the rule next to the calendar (“Weekdays only”): the dimmed color alone does not tell the user why.

API reference

Generated from the TypeScript source. className, style and children are accepted everywhere and omitted below.

Calendar

Calendar is a month grid for picking a date, several dates or a range, built on react-day-picker: set `mode` to "single", "multiple" or "range". For a date field in a form, open it from a button inside a Popover.

PropTypeDefault
buttonVariant"link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null

Button variant of the previous and next month arrows.

ghost

CalendarDayButton

CalendarDayButton is the button of one day. Calendar uses it already; it is exported to wrap or restyle through the `components.DayButton` prop.

PropTypeDefault
localePartial<DayPickerLocale>

Locale of the `data-day` attribute's date; Calendar passes its own.

-