Date & time picker

One DatePicker component that handles single dates, ranges, natural-language input, and paired time pickers.

Try “next monday”, “2 fridays from now”, or “last day of next month”.

Installation
$Terminal
npx shadcn@latest add https://sdk-components.thesqd.com/r/date-picker.json
Usage
Render `<DatePicker mode="single" />` or `<DatePicker mode="range" />`. Add `withTime` for a paired time input or `naturalLanguage` for free-text parsing.
TSImport
import { DatePicker } from "@/components/ui/date-picker";
TSExample
"use client";

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

export function Example() {
  return <DatePicker mode="single" label="Pick a date" />;
}
Composition
Anatomy of a date-picker compound.
DatePicker                       (master component — pick a mode)
├── mode="single"               (natural-language input by default)
│   ├── naturalLanguage={false} (swap to a plain outline trigger)
│   ├── withTime                (adds a paired native time input)
│   └── withTime + naturalLanguage  (parses "tomorrow at 3pm" into both)
└── mode="range"                (start + end date in one calendar)
    ├── withTime                (adds paired start/end time inputs)
    └── variant="inline"        (pencil-edit pill → two separate date fields)

Internally composes Popover + Button + Calendar; you don't wire them yourself.
Clear button
Every variant — single, range, natural-language input, and date + time — renders a `×` ghost Button inside the trigger when a value is selected. Click it to clear the date (and the input text where applicable) without re-opening the calendar. Same hover treatment as the chevron sitting next to it.
Date picker
Plain date picker — an outline trigger that opens the calendar in a popover.
Future dates only
Disables every day before today so the user can only pick an upcoming date.

Past dates are disabled.

Blacked-out dates
Pass `blockedDates={[…]}` an array of `Date` objects to disable specific days on the calendar — handy for company holidays, already-booked slots, or any non-contiguous blackout.

Blocked dates

  • · Mon, May 4, 2026
  • · Mon, May 11, 2026
  • · Thu, May 14, 2026
  • · Tue, May 19, 2026
  • · Wed, May 27, 2026
Range date picker
Select a start and end date in a single calendar.
Range date + time picker
Add `withTime` to `mode="range"` for a start and end time alongside the date range. `onChange` returns a `DateRange` with the times applied to `from`/`to`.
Inline range picker
Pass `variant="inline"` for a compact pill with a pencil edit affordance — the same display/edit feel as the Duration and File size inputs. Clicking expands it into two separate single-date pickers (Start and End), each its own regular calendar popover, with a ✓ to collapse back.
Inline range + time picker
Combine `variant="inline"` with `withTime` so each endpoint expands into its own date + time popover. `onChange` returns a `DateRange` whose `from`/`to` carry the selected times.
Natural language date picker
Parses phrases like “next monday” or “in 2 weeks”.

Try “next monday”, “2 fridays from now”, or “last day of next month”.

Date + time picker
Combined calendar and time input, side by side.
Inline date + time picker
`variant="inline"` renders a plain-text trigger (no field chrome) that opens the same calendar + time popover on click — for tables and inline-edit cells.
Natural language date + time picker
Free-text input that parses phrases like “tomorrow at 3pm” into both a date and a time, alongside the native time field.

Try “tomorrow at 3pm” or “next monday 10:30am”.

Quick selection chips
Pass `quickSelections` an array of presets (`"today"`, `"tomorrow"`, `"yesterday"`, `"next-week"`, `"next-month"`) and/or custom `{ label, getDate }` entries to render radio-style chips below the calendar (and time row). “Today” resolves against the user's current local day. Omit the prop entirely to hide the row.
Quick selection chips (range)
On `mode="range"`, `quickSelections` takes preset ranges (`"last-7-days"`, `"last-30-days"`, `"this-week"`, `"this-month"`, `"last-month"`, …) or custom `{ label, getRange }` entries that fill both endpoints in one click.
Error state
Pass `error="…"` to swap the helper text for the inline red message and apply the red focus/border treatment to the trigger.

Please pick a date.

API Reference
Props for the DatePicker component.

Common

PropTypeDefaultDescription
mode"single" | "range""single"Selection model.
labelReactNodeField label rendered above the trigger.
placeholderstring"Pick a date"Trigger text shown when no date is selected.
captionReactNodeCaption rendered beneath the trigger. Hidden when `error` is set.
errorReactNodeInline error message — swaps the helper line for the red treatment and applies the red focus/border ring on the trigger.
disabledMatcher | Matcher[]Disable specific days. e.g. `{ before: today }` for future-only.
blockedDatesDate[]Convenience array of individual days to black out (past or future). Merges with `disabled`. Free-text input ignores blocked dates.
minDateDateEarliest selectable day (inclusive). Days before it are disabled. Merges with `disabled`/`blockedDates`.
maxDateDateLatest selectable day (inclusive). Days after it are disabled. Merges with `disabled`/`blockedDates`.
align"start" | "center" | "end""start"Popover alignment relative to the trigger.
withIconbooleantrueShow the calendar icon inside the trigger.

mode="single"

PropTypeDefaultDescription
valueDate | undefinedControlled selected date.
onChange(date: Date | undefined) => voidFires when the user picks a day (or a day + time).
withTimebooleanfalseRender a paired native time input next to the date trigger.
defaultTimestring"06:30"Initial 24-hour time when `withTime` is enabled.
naturalLanguagebooleantrueFree-text input that parses phrases via chrono-node. Pass `false` for a plain trigger.
quickSelectionsQuickSelection[]Radio-style chips below the calendar (and time row). Built-in presets: `"today"`, `"tomorrow"`, `"yesterday"`, `"next-week"`, `"next-month"`. Or pass custom `{ label, getDate: (now) => Date }` entries. "Today" resolves against the user's current local day. Omit to hide the row.

mode="range"

PropTypeDefaultDescription
valueDateRange | undefined{ from: Date; to?: Date } controlled range.
onChange(range: DateRange | undefined) => voidFires after each pick. The second pick completes the range.
withTimebooleanfalseRender paired start/end time inputs in the popover. `onChange` returns the range with each time applied to `from`/`to`.
defaultTimestring"09:00"Initial 24-hour start time when `withTime` is enabled.
defaultEndTimestring"17:00"Initial 24-hour end time when `withTime` is enabled.
variant"field" | "inline""field"`"inline"` renders a compact pencil-edit pill (the Duration / File size input feel) that expands into two separate single-date pickers for Start and End — each its own regular calendar popover, not a shared range calendar. The End field can't go before Start (and vice-versa); a backwards range typed via natural language surfaces an inline error and blocks collapse. Respects `minDate`/`maxDate`/`blockedDates`. Works with or without `withTime`.
quickSelectionsRangeQuickSelection[]Radio-style chips below the calendar that apply a preset *range* (both endpoints). Built-in presets: `"today"`, `"yesterday"`, `"last-7-days"`, `"last-30-days"`, `"this-week"`, `"this-month"`, `"last-month"`. Or pass custom `{ label, getRange: (now) => DateRange }` entries. Omit to hide the row.