# Date Picker

An accessible React date picker for Next.js and Tailwind CSS: a calendar in a popover for a date or a date range, with presets and Intl formatting.

Source: https://ui.fewtone.com/components/date-picker

## Installation

```bash
npx shadcn@latest add https://ui.fewtone.com/r/date-picker.json
```

## Usage

```tsx
"use client"

import { useState } from "react"
import { DatePicker } from "@/components/ui/date-picker"

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

  return (
    <div className="grid gap-2">
      <label htmlFor="due" className="text-sm font-medium">
        Due date
      </label>
      <DatePicker id="due" value={date} onChange={setDate} />
    </div>
  )
}
```

## API reference

### DatePicker

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `Date` | – | The picked day, when you control it. |
| `defaultValue` | `Date` | – | The day picked at first. |
| `onChange` | `(date: Date \| undefined) => void` | – | Called with the new day. |
| `name` | `string` | – | Submits the day with a form, as YYYY-MM-DD. |
| `numberOfMonths` | `number` | `1` | Months shown side by side. |
| `placeholder` | `string` | `"Pick a date"` | Shown in the trigger while nothing is picked. |
| `presets` | `{ label: string; value: T \| (() => T) }[]` | – | A short list beside the calendar. A function value is worked out when it's picked, so presets like Today never read the clock while rendering. |
| `locale` | `string` | `"en-US"` | A language tag for the trigger's date and the calendar's names. |
| `formatOptions` | `Intl.DateTimeFormatOptions` | `{ dateStyle: "medium" }` | How the trigger writes the date, as Intl.DateTimeFormat options. |
| `min` | `Date` | – | The earliest day that can be picked. |
| `max` | `Date` | – | The latest day that can be picked. |
| `disabledDates` | `Matcher \| Matcher[]` | – | Days that can't be picked: Calendar's disabled prop. |
| `weekStartsOn` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6` | `0` | 0 is Sunday. |
| `open` | `boolean` | – | Whether the calendar is open, when you control it. |
| `onOpenChange` | `(open: boolean) => void` | – | Called when it opens or closes. |
| `align` | `"start" \| "center" \| "end"` | `"start"` | How the popover lines up with the trigger. |
| `size` | `"sm" \| "md"` | `"md"` | The trigger's height, as Select's. |
| `today` | `Date` | – | The day marked as today. Defaults to the browser's today. |
| `labels` | `{ dialog?, presets?, previousMonth?, nextMonth?, selected? }` | – | The words it says, for other languages: the popup's name, the presets' name and Calendar's labels. |
| `disabled` | `boolean` | `false` | Prevents opening. |

### DateRangePicker

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `DateRange` | – | The picked range ({ from, to }), when you control it. |
| `defaultValue` | `DateRange` | – | The range picked at first. |
| `onChange` | `(range: DateRange \| undefined) => void` | – | Called with the new range: first with only its start, then with both ends. |
| `numberOfMonths` | `number` | `2` | Months shown side by side. |
| `placeholder` | `string` | `"Pick a date range"` | Shown in the trigger while nothing is picked. |
| `presets` | `{ label: string; value: T \| (() => T) }[]` | – | A short list beside the calendar. A function value is worked out when it's picked, so presets like Today never read the clock while rendering. |
| `locale` | `string` | `"en-US"` | A language tag for the trigger's date and the calendar's names. |
| `formatOptions` | `Intl.DateTimeFormatOptions` | `{ dateStyle: "medium" }` | How the trigger writes the date, as Intl.DateTimeFormat options. |
| `min` | `Date` | – | The earliest day that can be picked. |
| `max` | `Date` | – | The latest day that can be picked. |
| `disabledDates` | `Matcher \| Matcher[]` | – | Days that can't be picked: Calendar's disabled prop. |
| `weekStartsOn` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6` | `0` | 0 is Sunday. |
| `open` | `boolean` | – | Whether the calendar is open, when you control it. |
| `onOpenChange` | `(open: boolean) => void` | – | Called when it opens or closes. |
| `align` | `"start" \| "center" \| "end"` | `"start"` | How the popover lines up with the trigger. |
| `size` | `"sm" \| "md"` | `"md"` | The trigger's height, as Select's. |
| `today` | `Date` | – | The day marked as today. Defaults to the browser's today. |
| `labels` | `{ dialog?, presets?, previousMonth?, nextMonth?, selected? }` | – | The words it says, for other languages: the popup's name, the presets' name and Calendar's labels. |
| `disabled` | `boolean` | `false` | Prevents opening. |

**When to use it:** Use DatePicker for a date field in a form, and DateRangePicker for a start and an end (stays, reports, campaigns). Use Calendar to show a month on the page itself, and Input with type=date when a typed date is enough.
**Built on:** Radix Popover, Intl.DateTimeFormat.
**Accessibility pattern:** https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-datepicker/
**npm packages (installed by the CLI):** lucide-react@^1.52.0, motion@^14.0.0.
**Installs these FewTone UI components with it:** `calendar`, `popover`.

## Examples

### Date and date range

Open either and the focus lands on the picked day, ready for the arrow keys. Picking a day closes the date picker once the circle has landed, and the trigger's text swaps to the new date. The range picker shows two months and stays open while you pick.

```tsx
"use client";

import { DatePicker, DateRangePicker } from "@/components/ui/date-picker";

// Fixed days, made in the browser's time zone (a client module), so the server and the browser agree
const due = new Date(2026, 9, 20);
const stay = { from: new Date(2026, 9, 12), to: new Date(2026, 9, 18) };

/*
 * A date and a date range side by side, named underneath. Picking a day closes the first once the circle
 * lands; the second stays open while the band grows between the two days.
 */
export default function DatePickers() {
  return (
    <ul className="flex flex-wrap items-start justify-center gap-x-10 gap-y-6">
      <li className="flex flex-col items-center gap-2">
        <DatePicker aria-label="Due date" defaultValue={due} className="w-56" />
        <span className="text-xs text-muted-foreground">Date Picker</span>
      </li>
      <li className="flex flex-col items-center gap-2">
        <DateRangePicker aria-label="Stay" defaultValue={stay} className="w-64" />
        <span className="text-xs text-muted-foreground">Date Range Picker</span>
      </li>
    </ul>
  );
}
```

### Presets

A small list beside the calendar for the dates people pick most. Each works its date out from today when it's picked, and the calendar turns to it.

```tsx
"use client";

import type { DateRange } from "@/components/ui/calendar";
import { DatePicker, type DatePreset, DateRangePicker } from "@/components/ui/date-picker";
import { useId } from "react";

/** A day counted from today. Presets call it when they're picked, so nothing reads the clock while rendering. */
const fromToday = (days: number) => {
  const now = new Date();
  return new Date(now.getFullYear(), now.getMonth(), now.getDate() + days);
};

const days: DatePreset<Date>[] = [
  { label: "Today", value: () => fromToday(0) },
  { label: "Tomorrow", value: () => fromToday(1) },
  { label: "In a week", value: () => fromToday(7) },
  { label: "In a month", value: () => fromToday(30) },
];

const ranges: DatePreset<DateRange>[] = [
  { label: "Next 7 days", value: () => ({ from: fromToday(0), to: fromToday(6) }) },
  { label: "Next 14 days", value: () => ({ from: fromToday(0), to: fromToday(13) }) },
  { label: "Next 30 days", value: () => ({ from: fromToday(0), to: fromToday(29) }) },
];

/*
 * Presets beside the calendar, for the dates people pick most. Each works its date out from today when it's
 * picked; the calendar turns to it, the circle travels or the band grows, and the first picker closes.
 */
export default function DatePickerPresets() {
  const id = useId();
  return (
    <div className="grid w-full max-w-xs gap-4">
      <div className="grid gap-2">
        <label htmlFor={`${id}-follow-up`} className="text-sm font-medium">
          Follow up
        </label>
        <DatePicker id={`${id}-follow-up`} presets={days} />
      </div>
      <div className="grid gap-2">
        <label htmlFor={`${id}-campaign`} className="text-sm font-medium">
          Campaign
        </label>
        <DateRangePicker id={`${id}-campaign`} presets={ranges} />
      </div>
    </div>
  );
}
```

## Related

- [Calendar](https://ui.fewtone.com/components/calendar.md): An accessible React calendar for Next.js and Tailwind CSS: pick a day or a range, with full keyboard support, disabled days and Intl names. No date library.
- [Popover](https://ui.fewtone.com/components/popover.md): An accessible React popover for Next.js and Tailwind CSS: forms, settings and other rich content anchored to a button, built on Radix Popover.
- [Select](https://ui.fewtone.com/components/select.md): An accessible React select with a gliding highlight, groups, labels and a scrollable list, built on Radix Select.
