---
title: Date Picker
description: "A button that opens a calendar in a popover, for one date or a range."
sidebar:
  badge: New
---

```tsx
import {
  DatePicker,
  DatePickerContent,
  DatePickerTrigger,
} from '@/components/ui/date-picker';

export default function DatePickerDemo() {
  return (
    <DatePicker>
      <DatePickerTrigger />
      <DatePickerContent />
    </DatePicker>
  );
}
```

## Install

```bash
npx @madeui/cli add date-picker
```

Installs `react-day-picker` and `date-fns` alongside the component, plus the `calendar`, `button`, and `popover` components it composes.

## Usage

```tsx
import {
  DatePicker,
  DatePickerContent,
  DatePickerTrigger,
} from '@/components/ui/date-picker';
```

```tsx
<DatePicker>
  <DatePickerTrigger />
  <DatePickerContent />
</DatePicker>
```

## Composition

```tsx
<DatePicker>
  <DatePickerTrigger>
    <DatePickerValue />
  </DatePickerTrigger>
  <DatePickerContent>
    {/* optional presets / controls, rendered above the calendar */}
  </DatePickerContent>
</DatePicker>
```

## Range

The popover stays open until both ends of the range are chosen. Two months sit
side by side on wide screens; below `640px` the calendar falls back to a single
month so the popup still fits the viewport.

```tsx
'use client';

import * as React from 'react';

import { addDays } from 'date-fns';

import {
  DatePicker,
  DatePickerContent,
  DatePickerTrigger,
  type DateRange,
} from '@/components/ui/date-picker';

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

  return (
    <DatePicker mode="range" value={range} onValueChange={setRange}>
      <DatePickerTrigger />
      <DatePickerContent numberOfMonths={2} />
    </DatePicker>
  );
}
```

## Dropdown caption

```tsx
import {
  DatePicker,
  DatePickerContent,
  DatePickerTrigger,
} from '@/components/ui/date-picker';

export default function DatePickerDropdown() {
  return (
    <DatePicker placeholder="Date of birth">
      <DatePickerTrigger />
      <DatePickerContent
        captionLayout="dropdown"
        defaultMonth={new Date(1990, 0)}
        startMonth={new Date(1930, 0)}
        endMonth={new Date()}
      />
    </DatePicker>
  );
}
```

## Presets

Children of `DatePickerContent` render above the calendar; here a row of buttons
sets the controlled value. A preset is a complete choice, so the example also
controls `open` and closes the popover after the click.

```tsx
'use client';

import * as React from 'react';

import * as stylex from '@stylexjs/stylex';
import { addDays } from 'date-fns';

import { Button } from '@/components/ui/button';
import {
  DatePicker,
  DatePickerContent,
  DatePickerTrigger,
} from '@/components/ui/date-picker';
import { space } from '@/lib/constants.stylex';

const presets = [
  { label: 'Today', days: 0 },
  { label: 'Tomorrow', days: 1 },
  { label: 'In a week', days: 7 },
];

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

  return (
    <DatePicker
      open={open}
      onOpenChange={setOpen}
      value={date}
      onValueChange={setDate}
    >
      <DatePickerTrigger />
      <DatePickerContent>
        <div {...stylex.props(styles.presets)}>
          {presets.map(({ label, days }) => (
            <Button
              key={label}
              variant="outline"
              size="xs"
              onClick={() => {
                setDate(addDays(new Date(), days));
                // A preset is a complete choice, so it closes the popover the
                // same way picking a day in the calendar does.
                setOpen(false);
              }}
            >
              {label}
            </Button>
          ))}
        </div>
      </DatePickerContent>
    </DatePicker>
  );
}

const styles = stylex.create({
  presets: {
    display: 'flex',
    gap: space.s2,
  },
});
```

## Form

```tsx
'use client';

import * as React from 'react';

import * as stylex from '@stylexjs/stylex';
import { format } from 'date-fns';

import { Button } from '@/components/ui/button';
import {
  DatePicker,
  DatePickerContent,
  DatePickerTrigger,
} from '@/components/ui/date-picker';
import { Field, FieldDescription, FieldLabel } from '@/components/ui/field';
import { space, fontSize, container } from '@/lib/constants.stylex';
import { colors } from '@/lib/tokens.stylex';

export default function DatePickerForm() {
  const [date, setDate] = React.useState<Date | undefined>();
  const [submitted, setSubmitted] = React.useState<string | null>(null);

  return (
    <form
      onSubmit={(event) => {
        event.preventDefault();
        setSubmitted(date ? format(date, 'yyyy-MM-dd') : 'none');
      }}
      {...stylex.props(styles.form)}
    >
      <Field>
        <FieldLabel htmlFor="date-picker-form-trigger">Date of birth</FieldLabel>
        <DatePicker value={date} onValueChange={setDate}>
          <DatePickerTrigger id="date-picker-form-trigger" />
          <DatePickerContent captionLayout="dropdown" endMonth={new Date()} />
        </DatePicker>
        <FieldDescription>Used to calculate your age.</FieldDescription>
      </Field>
      <div {...stylex.props(styles.row)}>
        <Button type="submit">Submit</Button>
        {submitted && (
          <span {...stylex.props(styles.result)}>Submitted: {submitted}</span>
        )}
      </div>
    </form>
  );
}

const styles = stylex.create({
  form: {
    display: 'flex',
    flexDirection: 'column',
    gap: space.s4,
    width: container.sm,
  },
  row: {
    alignItems: 'center',
    display: 'flex',
    gap: space.s3,
  },
  result: {
    color: colors.mutedForeground,
    fontSize: fontSize.sm,
  },
});
```

## Disabled

```tsx
import {
  DatePicker,
  DatePickerContent,
  DatePickerTrigger,
} from '@/components/ui/date-picker';

export default function DatePickerDisabled() {
  return (
    <DatePicker disabled defaultValue={new Date()}>
      <DatePickerTrigger />
      <DatePickerContent />
    </DatePicker>
  );
}
```

## API reference

Composes [Popover](/docs/components/popover), [Button](/docs/components/button), and [Calendar](/docs/components/calendar). The selection state lives in `DatePicker`; the tables below cover the props each part adds — everything else is forwarded to the part it wraps.

### DatePicker (Root)

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | `'single' \| 'range'` | `'single'` | Selection mode. Decides the value type of the props below. |
| `value` | `Date \| undefined` (single) / `DateRange \| undefined` (range) | — | Controlled value. |
| `defaultValue` | same as `value` | — | Uncontrolled initial value. |
| `onValueChange` | `(value) => void` | — | Called with the new value on every selection. |
| `format` | `string` | `'PPP'` / `'PP'` (range) | `date-fns` format string used by `DatePickerValue`. A range prints two dates, so it defaults to the shorter form. |
| `locale` | `Locale` | — | `date-fns` locale, applied to the formatted value and the calendar. |
| `placeholder` | `string` | `'Pick a date'` / `'Pick a date range'` | Shown while nothing is selected. |
| `disabled` | `boolean` | `false` | Disables the trigger. |
| `open` / `defaultOpen` / `onOpenChange` | — | — | Forwarded to the Popover root. The popover closes itself after a single date is chosen, or once a range is complete. |

### DatePickerTrigger

Renders this library's `Button` as the popover trigger, with a leading calendar icon. Children default to `<DatePickerValue />`.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `ButtonVariant` | `'outline'` |  |
| `size` | `ButtonSize` | `'md'` |  |
| `style` | `StyleXStyles` | — | StyleX styles merged last — always win over the component's own styles. |

### DatePickerValue

A `<span>` with the formatted selection (range: `from – to`, or `from` alone while incomplete) or the placeholder. Muted while empty. Accepts `style` and all native `span` props.

### DatePickerContent

`PopoverContent` with the wired `Calendar` (`mode`, `selected`, `onSelect`, `locale`, and `defaultMonth` come from the root). `children` render above the calendar in a column.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `side` / `sideOffset` / `align` / `alignOffset` | — | `align="start"` | Popover positioning. |
| `size` | `'sm' \| 'md'` | `'md'` | Calendar cell size. |
| `style` | `StyleXStyles` | — | Merged last on the popup. |
| `numberOfMonths` | `number` | `1` | Months shown side by side. Clamped to one below `640px`, where a wider strip would not fit. |
| `autoFocus` | `boolean` | `true` | The calendar takes focus on open (the selected day, else today) so the arrow keys work straight away. |
| `resetOnSelect` | `boolean` | `true` | Range mode: a click on a complete range starts a new one instead of extending it. |
| any other `Calendar` prop | — | — | `captionLayout`, `disabled`, `startMonth`, `endMonth`, `showWeekNumber`, `min`, `max`, … are forwarded to `Calendar`. |

### Popup width

The popup is as wide as the calendar asks for, clamped on both sides:

- Never narrower than the trigger. A single month is narrower than the default
  trigger, so the popup stretches to `--anchor-width` and the calendar sits
  centred inside it.
- Never wider than `--available-width`, the room the viewport leaves next to
  the trigger. A two-month calendar is free to outgrow the trigger.

Set a width on the popup with `style` to override the rule, and on the trigger
to change what the popup matches.

### Styling

`DatePickerTrigger`, `DatePickerValue`, and `DatePickerContent` accept `style` (`StyleXStyles`, merged last so caller overrides always win).
