# Date Picker (/ui/components/react/date-picker)



<Playground component="date-picker" />

## Usage [#usage]

```tsx
import { DatePicker } from '@appica/ui-react/date-picker'
```

```tsx
<DatePicker mode="single" value={date} onValueChange={setDate} />
```

`DatePicker` combines a typeable [`DateField`](/ui/components/react/date-field) (and optional [`TimeField`](/ui/components/react/time-field)) with a [`Calendar`](/ui/components/react/calendar) in a [`Popover`](/ui/components/react/popover) — so a value can be typed or picked. It's a custom composition, not a Base UI primitive. The `value` shape follows `mode`: a `Date` for `single`, a `DateRange` (`{ from, to }`) for `range`, and `Date[]` for `multiple`. Leave `value`/`onValueChange` off for an uncontrolled picker.

## Examples [#examples]

### Single date [#single-date]

`mode="single"` (the default) with a `Date` value. Picking a day closes the popover.

```tsx
'use client'

import * as React from 'react'
import { DatePicker } from '@appica/ui-react/date-picker'

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

  return <DatePicker className="max-w-50" mode="single" value={date} onValueChange={setDate} />
}
```

### Date & time [#date--time]

Add `showTime` to pair a [`TimeField`](/ui/components/react/time-field) with the date so the value carries hours and minutes. Use `timeFormat` to switch to 12-hour or drop seconds.

```tsx
'use client'

import * as React from 'react'
import { DatePicker } from '@appica/ui-react/date-picker'

export default function DatePickerWithTime() {
  const [date, setDate] = React.useState<Date | undefined>(new Date(2026, 5, 23, 9, 30, 0))

  return <DatePicker className="max-w-60" mode="single" showTime value={date} onValueChange={setDate} />
}
```

### Date range [#date-range]

`mode="range"` selects a span between two dates; the value is a `DateRange`.

```tsx
'use client'

import * as React from 'react'
import { DatePicker, type DateRange } from '@appica/ui-react/date-picker'

export default function DatePickerRange() {
  const [range, setRange] = React.useState<DateRange | undefined>({
    from: new Date(2026, 5, 10),
    to: new Date(2026, 5, 16),
  })

  return <DatePicker className="max-w-68" mode="range" value={range} onValueChange={setRange} />
}
```

### Multiple dates [#multiple-dates]

`mode="multiple"` collects an array of dates into a read-only summary field. Add `clearable` to reset, and `formatValue` to customize the summary text.

```tsx
'use client'

import * as React from 'react'
import { DatePicker } from '@appica/ui-react/date-picker'

export default function DatePickerMultiple() {
  const [days, setDays] = React.useState<Date[] | undefined>([
    new Date(2026, 5, 10),
    new Date(2026, 5, 15),
    new Date(2026, 5, 22),
  ])

  return (
    <DatePicker
      className="max-w-60"
      mode="multiple"
      clearable
      value={days}
      onValueChange={setDays}
      placeholder="Pick dates"
    />
  )
}
```

### Disabled dates [#disabled-dates]

`disabledDates` takes any react-day-picker [`Matcher`](https://daypicker.dev/api/type-aliases/Matcher) (or an array of them) — block past dates, weekends, specific days, or a predicate.

```tsx
'use client'

import * as React from 'react'
import { DatePicker } from '@appica/ui-react/date-picker'

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

  return (
    <DatePicker
      className="max-w-50"
      mode="single"
      value={date}
      onValueChange={setDate}
      disabledDates={[{ before: new Date() }, { dayOfWeek: [0, 6] }]}
    />
  )
}
```

### Variants [#variants]

```tsx
import { DatePicker } from '@appica/ui-react/date-picker'

const variants = ['outline', 'soft'] as const

export default function DatePickerVariants() {
  return (
    <div className="flex max-w-50 flex-wrap items-center gap-4">
      {variants.map((variant) => (
        <DatePicker key={variant} mode="single" variant={variant} defaultValue={new Date(2026, 5, 23)} />
      ))}
    </div>
  )
}
```

### Sizes [#sizes]

```tsx
import { DatePicker } from '@appica/ui-react/date-picker'

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

export default function DatePickerSizes() {
  return (
    <div className="flex max-w-50 flex-wrap items-center gap-4">
      {sizes.map((size) => (
        <DatePicker key={size} mode="single" size={size} defaultValue={new Date(2026, 5, 23)} />
      ))}
    </div>
  )
}
```

### Disabled & error states [#disabled--error-states]

`disabled` blocks the whole control; `aria-invalid` drives the `data-invalid` error styling and flags the field for assistive tech. The picker also inherits `disabled` and the error state from a surrounding [`Field`](/ui/components/react/field), so wrapping it in a `Field` marked `invalid` produces the same styling and is the better choice when you're also rendering a validation message.

```tsx
import { DatePicker } from '@appica/ui-react/date-picker'

export default function DatePickerStates() {
  return (
    <div className="flex w-full max-w-50 flex-col gap-4">
      <DatePicker defaultValue={new Date(2026, 5, 23)} disabled />
      <DatePicker defaultValue={new Date(2026, 5, 23)} aria-invalid />
    </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="date-picker" />

## API reference [#api-reference]

### Value [#value]

| Prop            | <ColMinWidth width="200">Type</ColMinWidth>    | Default    | <ColMinWidth width="200">Description</ColMinWidth>                   |
| --------------- | ---------------------------------------------- | ---------- | -------------------------------------------------------------------- |
| `mode`          | <Code>'single' \| 'range' \| 'multiple'</Code> | `'single'` | Selection behavior; determines the `value` shape.                    |
| `value`         | <Code>Date \| DateRange \| Date\[]</Code>      | —          | Controlled value; shape matches `mode`. Pair with `onValueChange`.   |
| `defaultValue`  | <Code>Date \| DateRange \| Date\[]</Code>      | —          | Uncontrolled initial value.                                          |
| `onValueChange` | <Code>(value) => void</Code>                   | —          | Fires when the selection changes; the argument shape matches `mode`. |

### Display [#display]

| Prop                    | <ColMinWidth width="200">Type</ColMinWidth>          | Default           | <ColMinWidth width="200">Description</ColMinWidth>                                                                        |
| ----------------------- | ---------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `variant`               | <Code>'outline' \| 'soft'</Code>                     | `'outline'`       | Field appearance — bordered or filled.                                                                                    |
| `size`                  | <Code>'sm' \| 'md' \| 'lg'</Code>                    | `'md'`            | Height, padding, and calendar cell scale.                                                                                 |
| `showTime`              | `boolean`                                            | `false`           | Add a `TimeField` (single/range modes).                                                                                   |
| `dateFormat`            | `string`                                             | `'MM/dd/yyyy'`    | date-fns format for the date field(s).                                                                                    |
| `timeFormat`            | `string`                                             | `'HH:mm:ss'`      | date-fns format for the time field(s).                                                                                    |
| `placeholder`           | `string`                                             | —                 | Placeholder for the multiple-mode summary field.                                                                          |
| `clearable`             | `boolean`                                            | `false`           | Show a clear button (multiple mode).                                                                                      |
| `formatValue`           | <Code>(value: Date\[] \| undefined) => string</Code> | —                 | Customize the multiple-mode summary text.                                                                                 |
| `rangeSeparator`        | `ReactNode`                                          | `'–'`             | Separator between the range's two fields.                                                                                 |
| `startSlot` / `endSlot` | `ReactNode`                                          | —                 | Adornments rendered inside the field frame.                                                                               |
| `triggerIcon`           | `ReactNode`                                          | calendar icon     | Icon for the popover trigger button.                                                                                      |
| `triggerAriaLabel`      | `string`                                             | `'Open calendar'` | Accessible label for the trigger button.                                                                                  |
| `className`             | `string`                                             | —                 | Classes on the root wrapper — use for footprint/layout (e.g. `max-w-60`). Merged via `tailwind-merge`.                    |
| `inputClassName`        | `string`                                             | —                 | Classes on the inner field box — use to override field styling (border, radius, background). Merged via `tailwind-merge`. |

### Behavior & state [#behavior--state]

| Prop            | <ColMinWidth width="200">Type</ColMinWidth> | Default | <ColMinWidth width="200">Description</ColMinWidth>                                                                                 |
| --------------- | ------------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `disabled`      | `boolean`                                   | `false` | Disable the whole control.                                                                                                         |
| `disabledDates` | <Code>Matcher \| Matcher\[]</Code>          | —       | Dates that can't be selected in the calendar.                                                                                      |
| `readOnly`      | `boolean`                                   | `false` | Make the typeable fields read-only.                                                                                                |
| `required`      | `boolean`                                   | `false` | Mark the form field as required.                                                                                                   |
| `name`          | `string`                                    | —       | Hidden input name for form submission. `range` mode emits `name[from]` / `name[to]`; with `showTime` the value is a full datetime. |
| `aria-invalid`  | `boolean`                                   | —       | Flag the field invalid — sets `data-invalid` for styling + conveys it to assistive tech.                                           |
| `closeOnSelect` | `boolean`                                   | auto    | Override auto-close. By default only single mode closes on pick; range and multiple stay open (dismiss on outside-click/Escape).   |

### Popover [#popover]

| Prop           | <ColMinWidth width="200">Type</ColMinWidth>         | Default    | <ColMinWidth width="200">Description</ColMinWidth>                                      |
| -------------- | --------------------------------------------------- | ---------- | --------------------------------------------------------------------------------------- |
| `open`         | `boolean`                                           | —          | Controlled open state. Pair with `onOpenChange`.                                        |
| `defaultOpen`  | `boolean`                                           | `false`    | Uncontrolled initial open state.                                                        |
| `onOpenChange` | <Code>(open: boolean) => void</Code>                | —          | Fires when the popover opens or closes.                                                 |
| `side`         | <Code>'top' \| 'bottom' \| 'left' \| 'right'</Code> | `'bottom'` | Preferred popover side.                                                                 |
| `align`        | <Code>'start' \| 'center' \| 'end'</Code>           | `'end'`    | Popover alignment.                                                                      |
| `sideOffset`   | `number`                                            | `6`        | Gap between the field and the popover.                                                  |
| `popoverProps` | <Code>Partial\<PopoverContentProps></Code>          | —          | Escape hatch forwarded to the inner `PopoverContent` (collision props, `className`, …). |

`DatePicker` also forwards [`Calendar`](/ui/components/react/calendar) configuration props — `captionLayout`, `numberOfMonths`, `weekStartsOn`, `showOutsideDays`, `startMonth`/`endMonth`, and more — to the calendar inside the popover.

## Accessibility [#accessibility]

* The date/time portions are typeable segmented fields (`role="spinbutton"` per segment); the calendar is a `role="grid"` opened from a labelled trigger button (`triggerAriaLabel`).
* The popover traps focus and restores it to the trigger on close; **Escape** closes it.
* A value can be entered entirely from the keyboard via the fields, or chosen in the calendar with arrow keys — see [`DateField`](/ui/components/react/date-field) and [`Calendar`](/ui/components/react/calendar).
* `disabled` blocks the whole control; `disabledDates` prevents specific days from being chosen.
* The error styling is driven by `data-invalid`; setting `aria-invalid` flags the field invalid (sets `data-invalid`) and conveys it to assistive tech. Pass `name` to submit the value through a hidden input.
