# Calendar (/ui/components/react/calendar)



<Playground component="calendar" />

## Usage [#usage]

```tsx
import { Calendar } from '@appica/ui-react/calendar'
```

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

`Calendar` wraps [react-day-picker](https://daypicker.dev)'s `DayPicker`, styled with Appica tokens and an added `size` prop. Set `mode` to choose the selection behavior, then drive the value with `selected` + `onSelect` (or omit both for an uncontrolled calendar). It defaults to Monday-first weeks and dropdown month/year navigation.

## Examples [#examples]

### Single date [#single-date]

The most common setup — `mode="single"` with a `Date | undefined` state.

```tsx
'use client'

import * as React from 'react'
import { Calendar } from '@appica/ui-react/calendar'

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

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

### Date range [#date-range]

`mode="range"` selects a contiguous span. The value is a `DateRange` (`{ from, to }`); the start, middle, and end days are styled as one block.

```tsx
'use client'

import * as React from 'react'
import { Calendar, type DateRange } from '@appica/ui-react/calendar'

export default function CalendarRange() {
  const [range, setRange] = React.useState<DateRange | undefined>(() => {
    const from = new Date()
    const to = new Date()
    to.setDate(to.getDate() + 5)
    return { from, to }
  })

  return <Calendar mode="range" selected={range} onSelect={setRange} />
}
```

### Multiple dates [#multiple-dates]

`mode="multiple"` collects an array of dates — click to add, click again to remove.

```tsx
'use client'

import * as React from 'react'
import { Calendar } from '@appica/ui-react/calendar'

export default function CalendarMultiple() {
  const [days, setDays] = React.useState<Date[] | undefined>(() => {
    const base = new Date()
    return [10, 15, 22].map((d) => new Date(base.getFullYear(), base.getMonth(), d))
  })

  return <Calendar mode="multiple" selected={days} onSelect={setDays} />
}
```

### Disabled dates [#disabled-dates]

`disabled` accepts any react-day-picker [`Matcher`](https://daypicker.dev/api/type-aliases/Matcher) — a date, a range, a set of weekdays, or a predicate. Disabled days are struck through and can't be selected. This example blocks every weekend.

```tsx
import { Calendar } from '@appica/ui-react/calendar'

export default function CalendarDisabled() {
  return <Calendar mode="single" disabled={{ dayOfWeek: [0, 6] }} />
}
```

### Sizes [#sizes]

The `size` prop scales the cell size and text. `captionLayout="label"` swaps the month/year dropdowns for a static label with arrow navigation.

```tsx
import { Calendar } from '@appica/ui-react/calendar'

const sizes = ['sm', 'md', 'lg'] as const

export default function CalendarSizes() {
  return (
    <div className="flex flex-wrap items-start justify-center gap-6">
      {sizes.map((size) => (
        <Calendar key={size} mode="single" size={size} captionLayout="label" />
      ))}
    </div>
  )
}
```

## RTL [#rtl]

Every Appica UI component supports right-to-left layouts out of the box. Set the `dir` attribute on a container (commonly your `<html>` element) so CSS logical properties resolve correctly, and wrap your tree in `DirectionProvider` so direction-aware behavior — roving focus, popup placement, and the like — follows the same direction.

```tsx
import { DirectionProvider } from '@appica/ui-react/providers/direction-provider'

export default function RootLayout({ children }) {
  return (
    <html lang="ar" dir="rtl">
      <body>
        <DirectionProvider dir="rtl">{children}</DirectionProvider>
      </body>
    </html>
  )
}
```

For setup details and caveats, see the [RTL guide](/ui/docs/react/rtl).

<RtlPreview component="calendar" />

## API reference [#api-reference]

| Prop                      | <ColMinWidth width="200">Type</ColMinWidth>                                 | Default         | <ColMinWidth width="220">Description</ColMinWidth>                       |
| ------------------------- | --------------------------------------------------------------------------- | --------------- | ------------------------------------------------------------------------ |
| `size`                    | <Code>'sm' \| 'md' \| 'lg'</Code>                                           | `'md'`          | Cell size and text scale (Appica extension).                             |
| `mode`                    | <Code>'single' \| 'multiple' \| 'range'</Code>                              | —               | Selection behavior. Omit for a display-only calendar.                    |
| `selected`                | <Code>Date \| Date\[] \| DateRange</Code>                                   | —               | Controlled selection; the shape matches `mode`. Pair with `onSelect`.    |
| `onSelect`                | <Code>(value, triggerDate, modifiers, event) => void</Code>                 | —               | Fires when the selection changes.                                        |
| `captionLayout`           | <Code>'dropdown' \| 'dropdown-months' \| 'dropdown-years' \| 'label'</Code> | `'dropdown'`    | Month/year header: select dropdowns, or a static label with arrows.      |
| `showOutsideDays`         | `boolean`                                                                   | `true`          | Render days from the adjacent months.                                    |
| `weekStartsOn`            | <Code>0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6</Code>                                | `1` (Mon)       | First day of the week.                                                   |
| `numberOfMonths`          | `number`                                                                    | `1`             | How many months to display.                                              |
| `month`                   | `Date`                                                                      | —               | Controlled displayed month. Pair with `onMonthChange`.                   |
| `onMonthChange`           | <Code>(month: Date) => void</Code>                                          | —               | Fires when the user navigates to another month.                          |
| `defaultMonth`            | `Date`                                                                      | current month   | Uncontrolled initial month to display.                                   |
| `startMonth` / `endMonth` | `Date`                                                                      | `1925` / `2050` | Bounds of the navigable range.                                           |
| `disabled`                | `Matcher`                                                                   | —               | Dates that can't be selected (a date, range, weekday set, or predicate). |
| `required`                | `boolean`                                                                   | `false`         | Prevent clearing the selection by re-clicking the selected day.          |
| `className`               | `string`                                                                    | —               | Extra classes on the root, merged via `tailwind-merge`.                  |

`Calendar` forwards every other [`DayPicker`](https://daypicker.dev/api/interfaces/DayPickerProps) prop — `modifiers`/`modifiersClassNames`, `formatters`/`labels`, `dir`, mode-specific options, and more — straight through. See the react-day-picker docs for the full API.

## Accessibility [#accessibility]

* Renders a `role="grid"` of day buttons; selected days expose `aria-selected`, and the current day is marked `today`.
* **←/→/↑/↓** move between days, **Page Up/Down** change months, and **Enter**/**Space** select — react-day-picker's full keyboard model.
* The previous/next buttons and the month/year dropdowns carry accessible labels.
* Disabled days are removed from selection and the pointer, and are visually struck through.
* Day hover/press transitions are wrapped in `motion-reduce:` variants, so they're skipped when the user prefers reduced motion.
