Date Picker
Winter sale campaign
Ads run from the first day at 00:00 to the last day at 23:59.
7 days of delivery
Installation
npx shadcn@latest add https://neonkit.heyaadi.com/r/date-picker.jsonUsage
import { DatePicker } from "@/components/ui/date-picker"
const [date, setDate] = React.useState<Date>()
<DatePicker label="Departure" value={date ?? null} onValueChange={setDate} />Examples
A single day in a form
formatOptions sets how the value reads, isDateDisabled blocks weekends, and name submits yyyy-mm-dd.
Relative presets
Presets are plain values, so build them from today. The one that matches the value is marked.
Sizes, error and disabled
API reference
Also accepts native button attributes, and the ref points at the trigger. className and style go to the field box.
| Prop | Type | Default |
|---|---|---|
mode | "single"One day, or a first and last day. | "single" |
value | DateControlled value, shaped by mode. Pass null for controlled with nothing picked. | None |
defaultValue | DateInitial value when uncontrolled. | None |
onValueChange | (value: Date | DateRange | undefined) => voidCalled with the picked day, a complete { from, to }, or undefined when cleared. A half-picked range is never reported. | None |
presets | { label: string; value: Date | DateRange }[]Shortcuts shown in a column beside the calendar (a row on phones). | None |
placeholder | ReactNodeShown while nothing is picked. Range mode defaults to "Pick a date range". | "Pick a date" |
formatOptions | Intl.DateTimeFormatOptionsHow the value reads in the field. Ranges use formatRange, so shared parts collapse: Oct 12 – 16, 2026. | { month: "short", day: "numeric", year: "numeric" } |
locale | stringBCP 47 tag for the field text and the calendar. | "en-US" |
minDate | DateEarlier days can't be picked. | None |
maxDate | DateLater days can't be picked. | None |
isDateDisabled | (date: Date) => booleanReturn true for days that can't be picked. A range can't run across one. | None |
weekStartsOn | 0First column of the calendar, 0 for Sunday. | from locale |
numberOfMonths | numberMonths in the calendar. | 2 for ranges on sm+, else 1 |
name | stringSubmits yyyy-mm-dd, or yyyy-mm-dd/yyyy-mm-dd for a range, with a surrounding form. A form reset puts back the starting date. | None |
required | booleanStops an empty submit with the browser's own message and turns the field red until a date is picked. | false |
label | ReactNodeVisible label. Names the trigger together with the value. | None |
description | ReactNodeHelper text under the field. | None |
error | ReactNodeRed invalid state and message. Replaces description. The calendar keeps its own color. | None |
disabled | booleanBlocks opening and leaves the value out of form data. | false |
color | NeonColorPalette name or hex. Carried into the popover. | inherited |
size | "sm"Field height: 32, 40 or 48px, matching Input. | "md" |
containerClassName | stringClasses for the outer block with the label and message. | None |
Accessibility
- When the browser rejects the field on submit (required, type, pattern), it turns red until it's edited or the form resets. The browser shows its own message.
- The trigger is a button with aria-haspopup="dialog" and aria-expanded, named by the label plus the current value, so a screen reader hears "Departure, Oct 14, 2026".
- Click, Enter, Space or Arrow Down opens the calendar with focus on the picked day. Escape or a click outside closes it and focus returns to the trigger.
- Inside, the calendar keeps its full keyboard grid. Tab moves between the presets, the month buttons and the active day, and tabbing past the end closes the panel.
- In range mode the panel says which end you're picking, and a polite live region announces it.
- The clear button is a separate button after the trigger, labelled "Clear date" or "Clear dates".
- Without a visible label, pass aria-label.