Components
DatePicker
A field-looking trigger that opens a Calendar in a popover, for picking one date or a range of dates in a form. It takes the same value props as Calendar.
Preview
import { DatePicker } from "@/components/ui/date-picker"
<DatePicker placeholder="Pick a date" className="max-w-md" />Usage
When to use
- Picking one date in a form, next to other fields, with the same field look as Input.
- Limiting the choice to real dates, with disabled days or a min and max date.
When not to use
- A whole month on the page when the date is the point of the screen. Use Calendar.
- Entering a date as text, such as a birth date the user knows by heart. Use Input.
Examples
In a form
The trigger has the field look, and the grid opens in a popover.
import { DatePicker } from "@/components/ui/date-picker"
<DatePicker placeholder="When does it start?" />Booking dates
A range over two months. The popover stays open after check-in and closes after check-out.
import { DatePicker } from "@/components/ui/date-picker"
<DatePicker mode="range" numberOfMonths={2} placeholder="Check-in to check-out" />Do and don't
Do
- Use DatePicker in forms, next to Input fields with the same md size.
- Name it with a Label pointing at its id, and describe it with aria-describedby, like any field.
- Disable days that can never be picked, such as past days or closed days, over a minDate and maxDate.
Don't
- Don't use it when the date is the whole screen; put a Calendar on the page instead.
- Don't put long text in the trigger; it shows one formatted date or one range.
- Don't use it for dates the user knows by heart, such as a birth date; an Input is faster.
Props
| Name | Type | Values | Default | Description |
|---|---|---|---|---|
| mode | enum | single | range | single | single picks one Date; range picks a start and an end ({ from, to }) and closes once both are picked. |
| value | string | - | - | The picked Date, or in range mode the picked range ({ from, to }), when you control it. An explicit undefined clears the selection; omitting value uses internal state. Pair it with onValueChange. |
| defaultValue | string | - | - | The Date, or in range mode the range, picked on first render, when you don't control it. |
| onValueChange | function | - | - | Called with the picked Date, or undefined when the user unpicks it. In range mode only with a complete range, or undefined when a one-day range is cleared. |
| disabled | boolean | - | false | true disables the trigger; a function receives a Date and disables single days in the Calendar, such as weekends or past dates. |
| minDate | string | - | - | No day before this Date can be picked or navigated to. |
| maxDate | string | - | - | No day after this Date can be picked or navigated to. |
| locale | string | - | - | A date-fns Locale object for the trigger text and the Calendar's month and weekday names. The default is enUS. |
| weekStartsOn | enum | 0 | 1 | - | The first day of the week in the Calendar: 0 Sunday or 1 Monday. Follows the locale when not set. |
| numberOfMonths | number | - | 1 | How many months the Calendar shows, side by side from sm and stacked below it. Use two for ranges that often cross a month. |
| rangeStartLabel | string | - | start of range | Range mode. Added to the start day's accessible name in the Calendar; translate it with the locale. |
| rangeEndLabel | string | - | end of range | Range mode. Added to the end day's accessible name in the Calendar; translate it with the locale. |
| placeholder | string | - | Pick a date | Text shown in the trigger while no date is picked. It also names the dialog when there is no aria-label. |
| modal | boolean | - | false | Whether the open popover blocks interaction with the rest of the page. |
Anatomy
| Part | Description |
|---|---|
| root | The trigger button: the same field look as Input md. |
| icon | A decorative Tabler Calendar icon at the start, 16px, secondary. |
| label | The picked date, formatted long; in range mode both ends in the medium format joined by an en dash (one date for a one-day range); or the placeholder in secondary text. |
| content | The Popover that holds a Calendar. |
Accessibility
Role: button
| Keys | Action |
|---|---|
| Space, Enter | On the trigger, opens the popover and focuses the selected day, or today. |
| Escape | Closes the popover and returns focus to the trigger. In range mode a start without an end is dropped and the value is unchanged. |
- The trigger opens a dialog named by aria-label or the placeholder; inside it, the Calendar grid keeps its own keyboard model.
- A picked date closes the dialog and focus returns to the trigger.
- In range mode the popover keeps a draft: it stays open after the start and sends onValueChange only with a complete range (then closes), or with undefined when the user clears a one-day range. A form value is never start-only. Closing with only a start drops it.
- The trigger accepts id, aria-label and aria-describedby, so a Label and a hint can name and describe it like any field.
Install
npx shadcn add https://werewolf-ui.vercel.app/r/date-picker.jsonThis also adds styles/werewolf.css, the tokens every component uses, and any other component it needs. Import that file once in your root CSS, after @import "tailwindcss", with a path relative to that CSS file (from app/globals.css it is @import "../styles/werewolf.css").