composed · Composed pattern

DatePicker

@devalok/shilp-sutra/composed/date-pickerView in Storybook
Live preview coming

Hand-curated previews ship in rolling waves. See it live in Storybook →

Reference

Props

DatePicker

value?: Date | null
onChange?: (date: Date | null) => void
placeholder: string (default: "Pick a date")
formatStr: string (default: "MMM d, yyyy")
minDate: Date
maxDate: Date
disabledDates: (date: Date) => boolean
className: string

DateRangePicker

startDate?: Date | null
endDate?: Date | null
onChange?: (range: { start: Date | null, end: Date | null }) => void
placeholder: string (default: "Pick a date range")
formatStr: string (default: "MMM d, yyyy")
minDate: Date
maxDate: Date
disabledDates: (date: Date) => boolean
presets: PresetKey[] (shows quick-select sidebar)
numberOfMonths: number (default: 1)

DateTimePicker

value: Date | null
onChange: (date: Date | null) => void
minDate: Date
maxDate: Date
disabledDates: (date: Date) => boolean
timeFormat: "12h" | "24h"
minuteStep: number
placeholder: string
className: string

TimePicker

value?: Date | null (time stored as a Date object)
onChange?: (date: Date) => void
format: "12h" | "24h" (default: "12h")
minuteStep: number (default: 1)
secondStep: number (default: 1)
showSeconds: boolean (default: false)
placeholder: string (default: "Pick a time")
disabled: boolean (default: false)
className: string

CalendarGrid

currentMonth: Date (REQUIRED)
selected?: Date | null
rangeStart?: Date | null
rangeEnd?: Date | null
hoverDate?: Date | null
onSelect: (date: Date) => void (REQUIRED)
onHover?: (date: Date | null) => void
onMonthChange: (date: Date) => void (REQUIRED)
onHeaderClick?: () => void
disabledDates: (date: Date) => boolean
minDate: Date
maxDate: Date
hidePrevNav: boolean
hideNextNav: boolean
events: CalendarEvent[] — { date: Date, color?: string, label?: string }

YearPicker

currentYear: number (REQUIRED)
selectedYear: number
onYearSelect: (year: number) => void (REQUIRED)
minDate: Date
maxDate: Date

MonthPicker

currentYear: number (REQUIRED)
selectedMonth: number (0-11)
onMonthSelect: (month: number) => void (REQUIRED)
minDate: Date
maxDate: Date

Presets

presets: PresetKey[] (REQUIRED)
onSelect: (start: Date, end: Date) => void (REQUIRED)
className: string

PresetKey: 'today' | 'yesterday' | 'last7days' | 'last30days' | 'thisMonth' | 'lastMonth' | 'thisYear'

useCalendar hook

useCalendar(initialMonth?: Date) => { currentMonth, setCurrentMonth, goToPreviousMonth, goToNextMonth, goToMonth, goToYear }

Defaults

DatePicker: placeholder="Pick a date", formatStr="MMM d, yyyy"
DateRangePicker: placeholder="Pick a date range", formatStr="MMM d, yyyy", numberOfMonths=1
TimePicker: format="12h", minuteStep=1, secondStep=1, showSeconds=false

Example

<DatePicker value={date} onChange={setDate} placeholder="Select date" />

<DateRangePicker
  startDate={start}
  endDate={end}
  onChange={({ start, end }) => { setStart(start); setEnd(end); }}
/>

<DateTimePicker value={dateTime} onChange={setDateTime} timeFormat="12h" minuteStep={15} />

<TimePicker value={time} onChange={setTime} format="24h" minuteStep={15} />

Composability

  • 7 sub-components in one kit: DatePicker, DateRangePicker, DateTimePicker, TimePicker, CalendarGrid, MonthPicker, YearPicker, Presets. Built on Popover (trigger + calendar content) + date-fns.
  • Layered abstractions:
    • High-level: DatePicker / DateRangePicker / DateTimePicker / TimePicker (use these 95% of the time — Popover-wrapped, single-field pickers with formatted value display).
    • Low-level: CalendarGrid / MonthPicker / YearPicker (use for custom picker layouts, inline calendars, dashboards with embedded date selection).
  • useCalendar hook manages month navigation state — use it when building custom picker UI (e.g. inline calendar with custom header).
  • Presets component is standalone — drop it next to a DateRangePicker's calendar for quick-select sidebars (Today / Last 7 days / This month / etc.).
  • Events on calendar: CalendarGrid accepts events: { date, color, label }[] — renders up to 3 colored dots per day cell. Useful for availability / booked-days highlights.
  • min/max/disabledDates are shared contract across the whole family — pass the same bounds config to any date picker variant.

Gotchas

  • TimePicker stores time inside a Date object — only hours/minutes/seconds are meaningful
  • CalendarGrid is a low-level building block — prefer DatePicker/DateRangePicker for most use cases
  • MonthPicker month values are 0-indexed (Jan=0, Dec=11) — same as JavaScript Date.getMonth()
  • YearPicker displays a 12-year grid based on the decade of currentYear
  • CalendarGrid supports up to 3 event dots per day cell
  • useCalendar is a convenience hook for managing calendar month state

Changes

v0.18.0

  • Fixed Added aria-label to DatePicker/DateRangePicker trigger buttons

v0.4.2

  • Changed DateRangePicker default formatStr from 'MMM d' to 'MMM d, yyyy'

v0.1.0

  • Added Initial release