DatePicker
A date field with a calendar, a list of times, or both behind a button inside it; a range picker on two months; and a time zone select — on the platform's popover, with no portal.
Import
JSX
import DatePicker, { DateRangePicker } from '@box-kite/react/components/datePicker';
import TimeZoneSelect from '@box-kite/react/components/timeZoneSelect';Usage
Check-in
mm
dd
yyyy
value:
nullJSX
<DatePicker label="Check-in" value={value} onValueChange={setValue} clearable />Type it or pick it
The field is a
DateField, so everything typed is in the reader's order and the value is the same ISO string with the same readings beside it. The button says what is chosen — Choose date, Oct 19, 2026 — and opening it puts focus on the chosen day, or today. Choosing closes the panel and hands focus back.Times and date-times
type="time" opens a list of times every step minutes, and type="datetime" puts the calendar and the list side by side: a day keeps the time already chosen, and the panel closes once a time is picked. The zone rules are DateField's — 'local' by default, 'utc' or any IANA zone, or 'none'.Publish at (UTC)
mm
dd
yyyy
hh
mm
AM
value:
nullutc:
nullJSX
<DatePicker type="datetime" zone="utc" label="Publish at (UTC)" step={15}
onValueChange={(value, { utc }) => schedule(utc)} />A range, with presets
DateRangePicker is two fields — start and end, each named for a screen reader — over a range calendar on two months. Its value is { start, end }, reported once both are filled in and in order; an end before its start marks the pair invalid rather than swapping it. presets puts shortcuts beside the calendar, and the date layer's arithmetic writes them.Stay
mm
dd
yyyy
mm
dd
yyyy
value:
nullJSX
<DateRangePicker label="Stay" startName="from" endName="to" clearable
presets={[{ label: 'Next 7 days', value: { start: today(), end: add(today(), { days: 6 })! } }]} />Choosing a zone
TimeZoneSelect is a Combobox over every zone the runtime knows, each with the offset it is at now, searchable by place, region or offset — chis, europe and +3 all find Chișinău. Its value is the IANA name every date component takes, so a meeting typed on another city's clock is two lines.Meeting
10
19
2026
02
30
PM
GMT+3
JSX
<TimeZoneSelect label="Time zone" value={zone} onValueChange={setZone} />
<DateField type="datetime" zone={zone} label="Meeting" />The panel
The top layer, no portal
The panel is the platform's popover: it paints above every stacking context, light dismiss and focus return are the browser's, and it inherits the theme and the direction around it. It hangs off the whole field, not off the button.
Mounted while open
A closed picker renders no calendar and no list of times, so a form with twenty of them carries twenty fields and nothing else.
Why not the native input
<input type="date"> follows the browser's language rather than the page's, has no zone, no range and no presets, and its popup cannot be styled. Textbox type="date" is still there for the cases it suits.Styling
The field is a
dateField and the panel a popover; what the picker adds is datePicker.trigger, datePicker.clear, datePicker.presets/preset and datePicker.times/time, whose selected variant is the chosen slot.DatePicker props
Everything below is this component’s own. All 235 of Box’s style props work on it too, and those are on /box rather than repeated here.
| Prop | Type | Default | What it does |
|---|---|---|---|
type | 'date' | 'time' | 'datetime' | 'date' | A day ('date', default), a time of day ('time'), or both ('datetime'). |
value | string | null | — | Controlled value, an ISO string of the picker's kind — see DateField. |
defaultValue | string | null | null | The value it starts with when the picker owns its state. |
onValueChange | ChangeHandler<string | null, DateChangeReason, DateChangeDetails> | — | Fires with the value and its readings — utc, the reader's local wall clock, a Date. |
zone | ZoneInput | 'none' | — | Whose wall clock a date-time is on. Default 'local'; 'none' for no zone at all. |
hourCycle | 12 | 24 | — | A 12- or 24-hour clock, in the field and the list of times. Default the locale's. |
granularity | FieldGranularity | — | Whether a time is edited to the minute (default) or the second. |
disambiguation | Disambiguation | — | What a wall-clock time a zone skips or repeats means. Default 'compatible'. |
name | string | — | What the value posts under in a form. |
step | number | 30 | Minutes between the times the list offers. Default 30. |
defaultTime | string | '00:00' | The time a day picked before any time takes. Default '00:00'. |
presets | readonly DatePreset[] | — | Shortcuts beside the calendar — { label: 'Today', value: today() }. |
showZone | boolean | — | Whether a date-time names its zone after the time. Default: when the zone is not the reader's own. |
locale | string | — | The language of the field, the calendar and the times. Default the reader's. |
min | string | null | — | The earliest value that is valid, and the first day the calendar offers. |
max | string | null | — | The latest. |
isDateUnavailable | (date: string) => boolean | — | Days nobody can choose. |
disabled | boolean | false | Not editable, not focusable and not openable. |
readOnly | boolean | false | Focusable and read out, but not editable, and the button does not open. |
required | boolean | — | The form is not valid until it is filled in — the browser's own check. |
invalid | boolean | — | Marks the value invalid whatever the bounds say. Default: invalid when out of bounds or unavailable. |
label | React.ReactNode | — | The picker's visible name, above the field. |
labelledBy | string | — | Names the field after an element already on the page, instead of label. |
describedBy | string | — | An element that describes the field — a hint, an error. |
open | boolean | — | Whether the panel is open. Controlled. |
defaultOpen | boolean | — | Whether it starts open, when the picker owns that itself. |
onOpenChange | ChangeHandler<boolean, DatePickerOpenReason> | — | Fires with the new state and why — the layer's four reasons, and 'select' when choosing closed it. |
closeOnSelect | boolean | true | Whether choosing closes the panel. Default true; a date-time closes once its time is chosen. |
firstDayOfWeek | number | — | The day the week starts on, 0 for Sunday. Default the locale's. |
weekNumbers | boolean | — | ISO week numbers down the calendar's side. |
side | AnchorSide | 'bottom' | Which side of the field the panel opens on. Default 'bottom'. |
align | AnchorAlign | 'start' | Which of the field's edges it lines up with. Default 'start'. |
clearable | boolean | false | A button inside the field that empties it. |
triggerLabel | string | — | The calendar button's name. Default 'Choose date', or 'Choose time'. |
clearLabel | string | 'Clear' | The clear button's name. Default 'Clear'. |
tabIndex | number | — | Every segment's and button's tab index. Default 0; -1 keeps the picker out of the page's tab order, which a grid cell wants. |
DatePicker keyboard
| Key | Result |
|---|---|
Enter / Space | On the button, opens the panel. |
Escape | Closes it and returns focus to the button. |
Up / Down | In the list of times, the time before or after. |
DatePicker accessibility
Implements the WAI-ARIA APG pattern.
- The button says what it does and what is chosen —
Choose date, Oct 19, 2026— and carriesaria-expandedandaria-haspopup="dialog"; the panel is arole="dialog"named by the picker's label. - Opening puts focus on the chosen day (or today) in the calendar, or on the chosen time in the list.
Swept with axe on every release, in this state:
DatePicker. No violations, with contrast and landmark rules left to a human. Screen-reader results are not published yet.DatePicker style tree
Every part the component draws is a node with a name, so a default can be restyled with
Box.components() instead of a selector — and a variant is a name too.datePickerdatePicker.partdatePicker.triggerdatePicker.cleardatePicker.presetsdatePicker.presetvariants: selecteddatePicker.timesdatePicker.timevariants: selectedDateRangePicker props
Everything below is this component’s own. All 235 of Box’s style props work on it too, and those are on /box rather than repeated here.
| Prop | Type | Default | What it does |
|---|---|---|---|
value | DateRange | null | — | Controlled value: both ends, YYYY-MM-DD, or null. |
defaultValue | DateRange | null | null | The span it starts with when the picker owns its value. |
onValueChange | ChangeHandler<DateRange | null, DateChangeReason> | — | Fires once both ends are filled in and in order, and with null when either is emptied. |
startName | string | — | What the start posts under in a form. |
endName | string | — | What the end posts under. |
startLabel | string | 'Start date' | The names a screen reader gives the two fields. Default 'Start date' and 'End date'. |
endLabel | string | 'End date' | The end field's name. Default 'End date'. |
months | 1 | 2 | 3 | 2 | Months side by side in the panel. Default 2. |
presets | readonly DatePreset<DateRange>[] | — | Shortcuts beside the calendar — { label: 'This week', value: { start, end } }. |
locale | string | — | The language of the field, the calendar and the times. Default the reader's. |
min | string | null | — | The earliest value that is valid, and the first day the calendar offers. |
max | string | null | — | The latest. |
isDateUnavailable | (date: string) => boolean | — | Days nobody can choose. |
disabled | boolean | false | Not editable, not focusable and not openable. |
readOnly | boolean | false | Focusable and read out, but not editable, and the button does not open. |
required | boolean | — | The form is not valid until it is filled in — the browser's own check. |
invalid | boolean | — | Marks the value invalid whatever the bounds say. Default: invalid when out of bounds or unavailable. |
label | React.ReactNode | — | The picker's visible name, above the field. |
labelledBy | string | — | Names the field after an element already on the page, instead of label. |
describedBy | string | — | An element that describes the field — a hint, an error. |
open | boolean | — | Whether the panel is open. Controlled. |
defaultOpen | boolean | — | Whether it starts open, when the picker owns that itself. |
onOpenChange | ChangeHandler<boolean, DatePickerOpenReason> | — | Fires with the new state and why — the layer's four reasons, and 'select' when choosing closed it. |
closeOnSelect | boolean | true | Whether choosing closes the panel. Default true; a date-time closes once its time is chosen. |
firstDayOfWeek | number | — | The day the week starts on, 0 for Sunday. Default the locale's. |
weekNumbers | boolean | — | ISO week numbers down the calendar's side. |
side | AnchorSide | 'bottom' | Which side of the field the panel opens on. Default 'bottom'. |
align | AnchorAlign | 'start' | Which of the field's edges it lines up with. Default 'start'. |
clearable | boolean | false | A button inside the field that empties it. |
triggerLabel | string | 'Choose dates' | The calendar button's name. Default 'Choose date', or 'Choose time'. |
clearLabel | string | 'Clear' | The clear button's name. Default 'Clear'. |
tabIndex | number | — | Every segment's and button's tab index. Default 0; -1 keeps the picker out of the page's tab order, which a grid cell wants. |
DateRangePicker keyboard
| Key | Result |
|---|---|
Enter / Space | On the button, opens the panel; in the calendar, anchors and then ends the range. |
Escape | Drops a half-picked range, then closes the panel. |
DateRangePicker accessibility
Implements the WAI-ARIA APG pattern.
- The two fields are named
startLabelandendLabeland grouped under the picker's label; the calendar marks every day of the spanaria-selected.
Swept with axe on every release, in this state:
DateRangePicker. No violations, with contrast and landmark rules left to a human. Screen-reader results are not published yet.DateRangePicker style tree
Every part the component draws is a node with a name, so a default can be restyled with
Box.components() instead of a selector — and a variant is a name too.datePickerdatePicker.partdatePicker.triggerdatePicker.cleardatePicker.presetsdatePicker.presetvariants: selecteddatePicker.timesdatePicker.timevariants: selectedTimeZoneSelect props
Everything below is this component’s own. All 235 of Box’s style props work on it too, and those are on /box rather than repeated here.
| Prop | Type | Default | What it does |
|---|---|---|---|
value | string | null | — | The chosen IANA zone (Europe/Chisinau, UTC), or null. Controlled. |
defaultValue | string | null | — | The zone it starts with when it owns its state. Default the reader's own. |
onValueChange | ChangeHandler<string | null, ComboboxValueReason> | — | Fires with the zone's IANA name. |
zones | readonly string[] | — | Only these zones, in IANA names. Default every zone the runtime knows, UTC first. |
label | React.ReactNode | — | The select's visible name. |
placeholder | string | — | The field's placeholder. Not a name — it is gone on the first keystroke. |
name | string | — | What the zone posts under in a form. |
disabled | boolean | — | Not editable and not openable, but still read out. |
props | Record<string, unknown> | — | Attributes for the <input> itself — an aria-label, an id. |
emptyText | React.ReactNode | 'No time zone matches' | What the list says when nothing matches. Default 'No time zone matches'. |
TimeZoneSelect keyboard
| Key | Result |
|---|---|
A printable character | Searches by place, region, IANA name or offset. |
Down / Up | Opens, or moves through what the search left. |
Enter | Chooses the highlighted zone. |
Escape | Closes the list, then clears what was typed. |
TimeZoneSelect accessibility
Implements the WAI-ARIA APG pattern.
- The APG editable combobox:
role="combobox"on the field, arole="listbox"of options, the active one named byaria-activedescendant. A thousand-odd options are windowed, so it opens in one frame.
Swept with axe on every release, in this state:
TimeZoneSelect. No violations, with contrast and landmark rules left to a human. Screen-reader results are not published yet.TimeZoneSelect style tree
Every part the component draws is a node with a name, so a default can be restyled with
Box.components() instead of a selector — and a variant is a name too.comboboxvariants: compactcombobox.labelcombobox.fieldcombobox.chipcombobox.removecombobox.iconcombobox.itemsvariants: closed, closedUpcombobox.windowcombobox.itemvariants: highlighted, createcombobox.message