Date Picker
A flattened date picker that wraps shadcn's Popover and Calendar. Supports single, multiple, and range selection modes via a single mode prop, a configurable date-fns format, and an optional inline-input mode for typing dates by hand.
Installation
With the @easy-shadcn namespace configured:
pnpm dlx shadcn@latest add @easy-shadcn/date-pickerOr install via the full URL (zero configuration):
pnpm dlx shadcn@latest add https://easy-shadcn.vercel.app/r/date-picker.jsonThe underlying popover, calendar, button, and input primitives (plus the react-day-picker and date-fns dependencies) are installed automatically alongside date-picker.
Usage
Single
import { useState } from "react"
import { DatePicker } from "@/components/easy/date-picker"
const [date, setDate] = useState<Date>()
<DatePicker value={date} onChange={setDate} />Multiple
Pick any number of independent dates from a single calendar panel. Clicking a selected day deselects it; the popover stays open so multiple picks are quick.
import { useState } from "react"
import { DatePicker } from "@/components/easy/date-picker"
const [dates, setDates] = useState<Date[]>()
<DatePicker mode="multiple" value={dates} onChange={setDates} />Range
Pick a contiguous from – to range from a single calendar panel. The first click sets from; the second sets to. The popover stays open until the user clicks outside.
import { useState } from "react"
import type { DateRange } from "react-day-picker"
import { DatePicker } from "@/components/easy/date-picker"
const [range, setRange] = useState<DateRange>()
<DatePicker mode="range" value={range} onChange={setRange} />With input
Enables a text input alongside the calendar trigger. Users can type a date string in the configured format and commit it with Enter or blur. Invalid input silently reverts to the last valid value.
<DatePicker withInput value={date} onChange={setDate} />Props
| Prop | Type | Default | Description |
|---|---|---|---|
mode | "single" | "multiple" | "range" | "single" | Selection mode. Controls the type of value / defaultValue / onChange. |
value | Date | Date[] | DateRange | - | Controlled selected value. Type narrows from mode. |
defaultValue | Date | Date[] | DateRange | - | Uncontrolled initial value. |
onChange | (value: Date | Date[] | DateRange | undefined) => void | - | Called when the selection changes. |
format | string | "PPP" | date-fns format token applied to the displayed value and used to parse user input. |
locale | Locale | - | date-fns locale forwarded to both the formatter and the Calendar. |
placeholder | string | mode-dependent | Trigger / input placeholder. |
disabled | boolean | false | Disables the trigger and (when present) the input. |
withInput | boolean | false | Render an <Input /> trigger that accepts manual typing. Single mode only. |
numberOfMonths | number | 1 | Number of months shown side-by-side in the Calendar. |
defaultMonth | Date | derived from the current value | Month displayed when the calendar first opens. |
startMonth | Date | - | Earliest month the user can navigate to. Bounds the year panel. |
endMonth | Date | - | Latest month the user can navigate to. |
open | boolean | - | Controlled popover open state. |
defaultOpen | boolean | false | Uncontrolled initial popover open state. |
onOpenChange | (open: boolean) => void | - | Called when the popover open state changes. |
className | ClassValue | - | Wrapper className (input mode) or trigger className (button mode). |
triggerClassName | ClassValue | - | Trigger element className. |
inputClassName | ClassValue | - | <Input /> className. Only used when withInput is true. |
iconButtonClassName | ClassValue | - | Calendar-icon button className. Only used when withInput is true. |
contentClassName | ClassValue | - | <PopoverContent /> className. |
calendarClassName | ClassValue | - | <Calendar /> className. |
Notes
- Default trigger width is
w-60(240px), matching the shadcn official date-picker block. Override withtriggerClassName(button mode) orclassName(input mode) to use a different width. withInputis single-mode only. The TypeScript types reject<DatePicker mode="multiple" withInput />and<DatePicker mode="range" withInput />at compile time. Parsing comma-separated dates or"from – to"strings is brittle (separator ambiguity, partial states, locale conflicts) and falls into the 5% bucket — drop down to the underlyingPopover+Calendarprimitives if you need that.- Auto-close behavior. In single mode the popover auto-closes after a date is picked. In multiple and range modes the popover stays open and the user closes it by clicking outside.
withInputanchor. WhenwithInputis enabled, the popover is anchored to the surrounding<Input />wrapper rather than the small calendar-icon button, so the calendar floats directly below the input.- Invalid input. When
withInputis enabled, blurring the input with an unparseable string silently reverts to the last valid display value. There is no inline error UI by design — wrap the component with your own field state if you need validation messaging. - Controlled vs uncontrolled.
value === undefinedis the controlled-mode detector, matching the convention used byModal. Passingvalue={undefined}keeps the component in controlled mode rather than silently switching to uncontrolled. - Localization. Pass
locale(adate-fnsLocale) to localize both the formatted display string (for tokens like"PPP") and the weekday / month labels rendered by the Calendar.