# Date Picker (/ui/components/react/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).


## API reference [#api-reference]

### Value [#value]

| Prop            | Type    | Default    | Description                   |
| --------------- | ---------------------------------------------- | ---------- | -------------------------------------------------------------------- |
| `mode`          | `'single' \| 'range' \| 'multiple'` | `'single'` | Selection behavior; determines the `value` shape.                    |
| `value`         | `Date \| DateRange \| Date\[]`      | -          | Controlled value; shape matches `mode`. Pair with `onValueChange`.   |
| `defaultValue`  | `Date \| DateRange \| Date\[]`      | -          | Uncontrolled initial value.                                          |
| `onValueChange` | `(value) => void`                   | -          | Fires when the selection changes; the argument shape matches `mode`. |
| `startSlot`     | `ReactNode`                                    | -          | Content pinned to the start edge.                                    |
| `endSlot`       | `ReactNode`                                    | -          | Content pinned to the end edge.                                      |

### Display [#display]

| Prop                    | Type          | Default           | Description                                                                        |
| ----------------------- | ---------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `variant`               | `'outline' \| 'soft'`                     | `'outline'`       | Field appearance - bordered or filled.                                                                                    |
| `size`                  | `'sm' \| 'md' \| 'lg'`                    | `'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`           | `(value: Date\[] \| undefined) => string` | -                 | 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            | Type | Default | Description                                                                                 |
| --------------- | ------------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `disabled`      | `boolean`                                   | `false` | Disable the whole control.                                                                                                         |
| `disabledDates` | `Matcher \| Matcher\[]`          | -       | 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           | Type         | Default    | Description                                      |
| -------------- | --------------------------------------------------- | ---------- | --------------------------------------------------------------------------------------- |
| `open`         | `boolean`                                           | -          | Controlled open state. Pair with `onOpenChange`.                                        |
| `defaultOpen`  | `boolean`                                           | `false`    | Uncontrolled initial open state.                                                        |
| `onOpenChange` | `(open: boolean) => void`                | -          | Fires when the popover opens or closes.                                                 |
| `side`         | `'top' \| 'bottom' \| 'left' \| 'right'` | `'bottom'` | Preferred popover side.                                                                 |
| `align`        | `'start' \| 'center' \| 'end'`           | `'end'`    | Popover alignment.                                                                      |
| `sideOffset`   | `number`                                            | `6`        | Gap between the field and the popover.                                                  |
| `popoverProps` | `Partial\<PopoverContentProps>`          | -          | 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 labeled 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.
