DatePicker
A trigger + popover calendar on Ark UI's date-picker machine, composing Calendar's grid and TimeInput's time row — range and withTime cover all four date combinations.
DatePicker is the flagship of the dates & time family — a styled trigger that opens a popover
calendar, composing Calendar's shared grid module and
TimeInput's shared time row. range and withTime cover all four date
combinations; DateRangePicker, DateTimePicker, and DateTimeRangePicker are thin named
presets pinning those two flags with a narrowed value type.
import { DatePicker } from '@42/ui-react/date-picker';
<DatePicker label="Start date" value={date} onChange={setDate} clearable />Playground
Component
Dir
Presets
Modes
Props
Code
<Component numOfMonths={1} minuteStep={1} timePlaceholder="--"/>Single date
The default shape — a native Date | null value, exactly like Calendar's own.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
30 | 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | 31 | 1 | 2 | 3 |
import { useState } from 'react';
import { DatePicker } from '@42/ui-react/date-picker';
const [date, setDate] = useState<Date | null>(null);
<DatePicker value={date} onChange={setDate} />Range
range selects a { start, end } pair instead of a single date — click once for the start,
again for the end.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
26 | 27 | 28 | 29 | 30 | 31 | 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | 1 | 2 | 3 | 4 | 5 |
import { useState } from 'react';
import { DateRangePicker, type DateRangeValue } from '@42/ui-react/date-picker';
const [range, setRange] = useState<DateRangeValue>({ start: null, end: null });
<DateRangePicker value={range} onChange={setRange} />With time
withTime adds an embedded HOUR/MINUTE row (TimeInput's own TimeRow) below the calendar
grid; the value carries the picked hour/minute on the same Date. Combine with range for all
four combinations.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
30 | 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | 31 | 1 | 2 | 3 |
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
26 | 27 | 28 | 29 | 30 | 31 | 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | 1 | 2 | 3 | 4 | 5 |
<DateTimePicker value={date} onChange={setDate} />
<DateTimeRangePicker value={range} onChange={setRange} />12h vs 24h follows the active locale by default (same rule as TimeInput) — override with
format={12 | 24} or the finer hourCycle escape hatch.
| L | M | M | J | V | S | D |
|---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | 1 | 2 | 3 | 4 |
// French defaults to 24h — no AM/PM segment — and formats the trigger's date/time accordingly.
<DateTimePicker locale="fr-FR" value={date} onChange={setDate} />Presets
presets renders a sidebar of quick-pick shortcuts. presets={true} ships a mode-appropriate
default set; pass an array for a fully custom list. DatePreset has three shapes: { value }
(one of Ark's own range preset strings), { range } (an explicit { start, end } | null, range
mode), or { date } (a single Date | null, single-date mode) — nothing stops mixing shapes for
the current mode, so keep custom entries matched to range.
Range presets
Default set: Today, Yesterday, This week, Last week, This month, Last month, This year, All time.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
26 | 27 | 28 | 29 | 30 | 31 | 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | 1 | 2 | 3 | 4 | 5 |
import { DateRangePicker, type DatePreset } from '@42/ui-react/date-picker';
// Default set
<DateRangePicker presets value={range} onChange={setRange} />
// Custom — mix Ark's own string presets with explicit ranges; `range: null` clears/unbounds.
const customPresets: DatePreset[] = [
{ label: 'This week', value: 'thisWeek' },
{ label: 'This year', value: 'thisYear' },
{ label: 'All time', range: null },
];
<DateRangePicker presets={customPresets} value={range} onChange={setRange} />Single-date presets
Default set: Today, Yesterday, Tomorrow, Next week — a different, forward-facing shape than the range set (a due date or appointment, not an analytics window), not a trimmed-down copy of it — "This week"/"This month" are inherently range-shaped and don't collapse to one date.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
30 | 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | 31 | 1 | 2 | 3 |
import { DatePicker, type DatePreset } from '@42/ui-react/date-picker';
// Default set
<DatePicker presets value={date} onChange={setDate} />
// Custom — `date: null` clears.
const customPresets: DatePreset[] = [
{ label: 'Next Friday', date: nextFriday },
{ label: 'Clear', date: null },
];
<DatePicker presets={customPresets} value={date} onChange={setDate} />Staged commit (withConfirm)
Without withConfirm, a single date commits (and closes) on pick, and a range commits on
its second click — both handled natively by Ark. With withConfirm, the popover stays open
after a pick: Cancel/Apply buttons appear, and only Apply calls the outer onChange — Cancel
reverts to the last committed value.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
30 | 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | 31 | 1 | 2 | 3 |
<DatePicker withConfirm value={date} onChange={setDate} />Translations
locale drives month/day names, first-day-of-week, and the embedded time row's 12h/24h default
automatically, but everything else in this popover — Ark's own accessible names, the
presets={true} default labels, Cancel/Apply, and Start/End time — is English by default.
translations is one prop covering all of it; it's partial, so unset keys keep their English text.
<DateRangePicker
locale="fr-FR"
presets
withConfirm
translations={{
presets: { today: "Aujourd'hui", thisWeek: 'Cette semaine' },
cancel: 'Annuler',
apply: 'Valider',
startTime: 'Début',
endTime: 'Fin',
}}
value={range}
onChange={setRange}
/>Restricting selection
isDateUnavailable, min, and max work exactly as they do on Calendar —
see its Restricting selection section for the full set
of composable predicate helpers (disablePast(), disableWeekends(), beforeDate(d), anyOf(...),
…), re-exported from this same @42/ui-react/date-picker barrel.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
30 | 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | 31 | 1 | 2 | 3 |
import { DatePicker, disablePast, disableWeekends, anyOf } from '@42/ui-react/date-picker';
<DatePicker isDateUnavailable={anyOf(disablePast(), disableWeekends())} />Fixed height
Same as Calendar — fixedWeeks always renders 6 week rows
(padding with the adjacent month's days) instead of however many the visible month(s) actually
span (4–6), keeping the popover's height constant while paging between months.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
30 | 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | 31 | 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
<DatePicker fixedWeeks />Aliases
Named presets pinning range/withTime — the same component, a narrowed value type at the call
site. See DatePicker's full API below; nothing about them is component-specific beyond
which flags they pin.
DateRangePicker===<DatePicker range />DateTimePicker===<DatePicker withTime />DateTimeRangePicker===<DatePicker range withTime />
Forms
name wires a hidden <input type="hidden"> into native form submission — ${name}Start /
${name}End under range — serialized as a plain YYYY-MM-DD (or YYYY-MM-DDTHH:mm:ss under
withTime), read directly off the machine's own value.
<DatePicker name="dueDate" />
<DateRangePicker name="period" />Using it as a DataTable date filter
DataTable's date / date-range column filters (meta.filter) render this
exact popover content — the calendar grid, presets rail, and (under withTime) time row —
composed directly from DatePicker's exported parts, not a separate implementation:
// data-table-filter/data-table-date-filter.tsx (simplified)
<DatePicker.Root
inline // the header cell's own Popover already owns the trigger/visibility
selectionMode="range" // both `date` and `date-range` are range-shaped UIs — see below
locale={locale}
timeZone={timeZone}
value={values} // parsed straight from the stored string bounds, see below
onValueChange={(details) => commit(details.value)}
>
<DatePicker.Context>
{(api) => (
<DatePicker.Content>
{presets && <DatePicker.Presets presets={presets} range timeZone={timeZone} />}
<DatePicker.Calendar numOfMonths={2} size="sm" timeZone={timeZone} />
{withTime && (
<DatePicker.TimePanelRow
value={api.value[0]}
onSetTime={(time) => api.setTime(time, 0)}
hourCycle={hourCycle}
/>
)}
</DatePicker.Content>
)}
</DatePicker.Context>
</DatePicker.Root>.Context (Ark's own context accessor) and .TimePanelRow (the bridge between TimeRow's
display-space props and api.setTime/api.value, distinct from the dumber .TimePanel =
TimeRow) are exported from this module specifically for this composition. The bridge stays
entirely in Ark's own DateValue space — the filter's stored "YYYY-MM-DD" /
"YYYY-MM-DDTHH:mm:ss" strings convert via @internationalized/date's parseDate/parseDateTime
inbound and this module's own exported serializeDateValue outbound, never round-tripping through
a native Date.
A calendar's range clicks are ordered (first click is the start, second the end) — a lone click
derives { op: "gte" }; unlike the column's old two-independent-inputs UI, there's no interaction
that produces an upper-bound-only { op: "lte" } value through this popover (the operator itself
is unaffected and still fully settable programmatically via controlled columnFilters). See
DataTable's own Date and date-range section for the full
column-facing API, including the optional presets field.
API
Prop
Type
Accessibility
- The trigger is a real
<button>witharia-haspopup="grid"andaria-expanded, all from Ark. - The popover grid inherits every keyboard/ARIA behavior
Calendarhas — arrow-key navigation,aria-disabledon unavailable days,aria-selected. - The embedded time row's HOUR/MINUTE fields and AM/PM toggle carry real accessible names, same
as standalone
TimeInput— visually hidden (sr-only) with a decorativeh/m/ssuffix shown in their place, not removed. - Cancel/Apply (under
withConfirm) are real<button>s with accessible names. - The portaled popover re-stamps
data-colorso the palette survives being rendered outside the trigger's own DOM subtree.
Time Input
HOUR/MINUTE (+ SECOND) + AM/PM time entry composed from NumberInput + SegmentGroup — Ark ships no time-picker machine, so this is the kit's own composed time field.
Badge
Compact inline label. Five variants × eleven colors, sharing Button's accent system but tuned for status chips and counters.