Form
DatePicker
Read-only date input that opens a month-view Calendar in a portal popover. The Calendar is also exported for inline use, with full keyboard grid navigation.
Import
import { DatePicker, Calendar } from "@zephora/react";Examples
Basic
<DatePicker aria-label="Delivery date" placeholder="Pick a date…" />
Controlled with min/max
Days outside minDate/maxDate are disabled in the calendar.
Selected: none
const [date, setDate] = React.useState<Date | null>(null);
const today = new Date();
const inAWeek = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 7);
<DatePicker
aria-label="Appointment"
value={date}
onValueChange={setDate}
minDate={today}
maxDate={inAWeek}
/>
<p>Selected: {date ? date.toDateString() : "none"}</p>Inline Calendar
Use the exported Calendar directly, without the popover.
SunMonTueWedThuFriSat
const [date, setDate] = React.useState<Date | null>(new Date());
<Calendar value={date} onValueChange={setDate} />Locale
Month and weekday names follow the BCP 47 `locale`.
<DatePicker aria-label="Tarih" locale="tr" placeholder="Tarih seçin…" />
Range
`mode="range"` selects a `[start, end]` pair; `allowClear` adds a clear button and `presets` render quick-pick buttons under the calendar.
… → …
const [range, setRange] = React.useState<[Date | null, Date | null]>([null, null]);
const today = new Date();
const weekAway = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 7);
<DatePicker
aria-label="Stay"
mode="range"
rangeValue={range}
onRangeChange={setRange}
allowClear
presets={[{ label: "Next 7 days", value: [today, weekAway] }]}
/>
<p>
{range[0] ? range[0].toDateString() : "…"} → {range[1] ? range[1].toDateString() : "…"}
</p>With time
`showTime` renders hour/minute selects under the day grid and includes the time in the display; `minuteStep` sets the minute granularity. `disabledDate` blocks individual days (weekends here) and `firstDayOfWeek` starts the week on Monday.
<DatePicker
aria-label="Meeting"
showTime
minuteStep={15}
firstDayOfWeek={1}
disabledDate={(date) => date.getDay() === 0 || date.getDay() === 6}
format={(date) => date.toLocaleString()}
/>API
DatePicker props
| Prop | Type | Default | Description |
|---|---|---|---|
value | Date | null | — | Controlled selected date (single mode). |
defaultValue | Date | null | — | Initial selected date in uncontrolled mode (single mode). |
onValueChange | (date: Date | null) => void | — | Called with the newly selected date (single mode). |
mode | "single" | "range" | "single" | Selection mode. |
rangeValue | DateRange | — | Controlled selected range (range mode) — [start, end], either side may be null while in progress. |
defaultRangeValue | DateRange | — | Initial range in uncontrolled mode (range mode). |
onRangeChange | (range: DateRange) => void | — | Called whenever the range changes (range mode). |
minDate | Date | — | Earliest selectable day (inclusive). |
maxDate | Date | — | Latest selectable day (inclusive). |
disabledDate | (date: Date) => boolean | — | Disables individual days beyond minDate/maxDate. |
firstDayOfWeek | number | from locale | First day of the week: 0 = Sunday, 1 = Monday… |
showTime | boolean | false | Renders hour/minute selects and includes the time in the display. |
minuteStep | number | 5 | Minute select granularity when showTime is set. |
presets | CalendarPreset[] | — | Quick-pick buttons shown under the calendar: { label, value } with a Date or a [start, end] tuple. |
allowClear | boolean | false | Renders a clear button inside the trigger when a value is set. |
format | (date: Date) => string | — | Overrides the display formatting of dates in the trigger. |
locale | string | "en" | BCP 47 locale used for formatting. |
placeholder | string | "Select date…" | Text shown when no date is selected. |
size | "sm" | "md" | "lg" | "md" | Input scale. |
invalid | boolean | false | Marks the input invalid (aria-invalid). |
disabled | boolean | false | Disables the input. |
aria-label / aria-labelledby | string | — | Accessible name for the input. |
unstyled | boolean | false | Headless mode — drops Zephora classes so your own CSS can style it. |
…rest | HTMLAttributes<HTMLDivElement> | — | Forwarded to the root wrapper element. |
Calendar props
| Prop | Type | Default | Description |
|---|---|---|---|
value | Date | null | — | Controlled selected date (single mode). |
defaultValue | Date | null | — | Initial selected date in uncontrolled mode (single mode). |
onValueChange | (date: Date | null) => void | — | Called with the newly selected date (single mode). |
mode | "single" | "range" | "single" | Selection mode. |
rangeValue / defaultRangeValue / onRangeChange | DateRange / (range: DateRange) => void | — | Range-mode value, initial value and change callback. |
minDate | Date | — | Earliest selectable day (inclusive). |
maxDate | Date | — | Latest selectable day (inclusive). |
disabledDate | (date: Date) => boolean | — | Disables individual days beyond minDate/maxDate. |
firstDayOfWeek | number | from locale | First day of the week: 0 = Sunday, 1 = Monday… |
showTime / minuteStep | boolean / number | false / 5 | Renders hour/minute selects under the day grid. |
presets | CalendarPreset[] | — | Quick-pick buttons shown under the calendar. |
locale | string | "en" | BCP 47 locale for month/weekday names. |
unstyled | boolean | false | Headless mode — drops Zephora classes so your own CSS can style it. |
…rest | HTMLAttributes<HTMLDivElement> | — | Forwarded to the calendar wrapper element. |
Keyboard
| Key | Action |
|---|---|
Enter / Space / Arrow Down (input) | Opens the calendar popover. |
Escape | Closes the popover and returns focus to the input. |
Arrow keys (grid) | Move focus by one day (left/right) or one week (up/down). |
Home / End (grid) | Moves to the start / end of the week. |
Page Up / Page Down (grid) | Moves to the previous / next month. |
Enter / Space (grid) | Selects the focused day. |