Experience System

Date & Time Pickers

Registry

Segmented date, range, and date & time fields with calendar popovers—Blue Yonder registry recipes built on Field, InputGroup, Calendar, and Popover from @by/experience-system.

Current: 4/21/2026

Installation

Date & Time Pickers on this page are @by-es registry components materialized with npx shadcn@latest add @by-es/<name>. They depend on @by/experience-system (and @by/icons where noted). Each picker below ships its own Command/Manual install steps in its Installation block — the Command tab vendors the recipe and runtime deps via shadcn; the Manual tab lists runtime packages, source files to copy into your project, and any registry dependencies to add separately.

In this monorepo, depend on the workspace package (for example via workspace:* or your catalog) so imports resolve to packages/experience-system. Configure the Registry in your app before adding recipes.

Composition

Registry pickers compose Field (label, helper, error), InputGroup (segments, triggers, and optional read-only display), Popover + Calendar from @by/experience-system, and optional Button, DropdownMenu, Badge, or Separator depending on the recipe:

Field
├── FieldLabel (optional)
├── InputGroup
│   ├── Segment inputs and/or read-only display
│   ├── InputGroupAddon (optional — calendar trigger, actions)
│   └── Popover → Calendar (single, range, or multi-month)
└── FieldDescription | FieldError (optional)

Underlying grids and day semantics follow react-day-picker through the Experience System Calendar; see Calendar for selection modes and styling hooks.

Usage

After you add a recipe with npx shadcn@latest add @by-es/<item>, import the generated module from your registry alias (often @/components/ui/<kebab-name>). Each picker is a client component—use 'use client' in the file that imports it, or render it from another client component.

'use client';

import { DatePicker } from '@/components/ui/date-picker';
import * as React from 'react';

export function ShipDateField() {
  const [value, setValue] = React.useState<Date | undefined>();
  return <DatePicker label="Ship date" value={value} onChange={setValue} />;
}

Wire label, helperText, name (hidden ISO field where provided), and validation error / incomplete states as you would for other Field-backed controls.

Examples

The live preview at the top of this page is a controlled single-date field with helper text showing the committed value.

Single date with min and max

Restrict selection to the current calendar month using minDate and maxDate.

Only dates in the current month are selectable.

Single date with past dates disabled

Use disablePast so earlier days are unavailable in the calendar and invalid when typed.

Past dates are disabled in the calendar and flagged as invalid when typed.

Read-only range with one calendar

A formatted read-only range opens a single calendar in mode="range"; onChange fires when both endpoints are committed.

Range: —

Date & time

Segmented date and time with a calendar popover; confirm the date portion with OK before the popover closes.

Current: 4/16/2022, 7:00:00 PM

Date & time with min and max

The same date-time field with minDate / maxDate on the date portion.

Only dates in the current month are selectable.

API Reference

The tables below describe the public props exported from each registry recipe under apps/experience-system-registry/registry/by/components/. Additional behavior (for example Calendar passthroughs) follows react-day-picker and the Experience System Calendar wrapper.

DatePicker

PropTypeDefault
valueDate
defaultValueDate
onChange(date: Date | undefined) => void
formatMM/dd/yyyy | dd/MM/yyyy | yyyy-MM-ddMM/dd/yyyy
minDateDate
maxDateDate
disableFuturebooleanfalse
disablePastbooleanfalse
disableDate(date: Date) => boolean
closeOnSelectbooleantrue
showDaysOutsideCurrentMonthboolean
displayWeekNumberboolean
disableHighlightTodayboolean
autoFocusbooleanfalse
disabledbooleanfalse
readOnlybooleanfalse
errorbooleanfalse
helperTextReact.ReactNode
requiredbooleanfalse
namestring
idstring
labelReact.ReactNode
classNamestring
refReact.Ref<HTMLInputElement>
Data attributeValues
data-slotdate-picker (on Field)

DateRangePicker

PropTypeDefault
valueDateRange
defaultValueDateRange
onChange(range: DateRange | undefined) => void
minDateDate
maxDateDate
disableFutureboolean
disablePastboolean
disableDate(date: Date) => boolean
closeOnCompletebooleantrue
showDaysOutsideCurrentMonthboolean
numberOfMonthsnumber1
formatRange(range: { from: Date; to?: Date }) => string
placeholderstring
autoFocusboolean
disabledboolean
readOnlyboolean
errorboolean
helperTextReact.ReactNode
requiredboolean
namestring
idstring
labelReact.ReactNode
classNamestring
refReact.Ref<HTMLInputElement>

DateTimePicker

PropTypeDefault
valueDate
defaultValueDate
onChange(date: Date | undefined) => void
format12h | 24h12h
minDateDate
maxDateDate
disabledbooleanfalse
readOnlybooleanfalse
errorbooleanfalse
helperTextReact.ReactNode
requiredbooleanfalse
namestring
idstring
labelReact.ReactNode
classNamestring
refReact.Ref<HTMLInputElement>

TimePicker

TimePicker is a discriminated union: with format="12h" (default) the value carries hour, minute, and period; with format="24h" the value omits period. See TimePickerProps in apps/experience-system-registry/registry/by/components/time-picker.tsx for the full typing and onChange signatures.

Accessibility

Provide a visible label (or equivalent aria-label on the control) for every picker. Segmented inputs use spinbutton-style stepping and arrow-key navigation between segments; calendar triggers expose aria-expanded and open a Popover dialog region. Pair helperText with error so validation messages are associated via Field. For grid semantics and day navigation, follow react-day-picker accessibility and test with your target locales and week-start settings.

Source in the repo: registry implementations under apps/experience-system-registry/registry/by/components/ (date-picker.tsx, date-range-picker.tsx, date-time-picker.tsx, and related recipes).

Registry examples

These @by-es items are registry-only composites. Live previews use the same sources as the Experience System registry; View code shows post-shadcn add imports (often @/components/ui/...). See Registry for components.json and REGISTRY_TOKEN.

Date Picker

Date input with segmented typing (MM/DD/YYYY and friends), calendar popover, min/max/disable-date constraints, and parent-facing data-value / data-valid state using Field, InputGroup, Calendar, and Popover from @by/experience-system.

After shadcn add, import DatePicker from your registry path (for example @/components/ui/date-picker). Use View code on the preview above for a copy-pasteable snippet.

Run the shadcn CLI with your package manager. It vendors the recipe into components/ui/date-picker.tsx and installs the runtime dependencies (@by/experience-system, …) automatically:

pnpm dlx shadcn@latest add @by-es/date-picker

In this monorepo, depend on the workspace package (for example via workspace:* or your catalog) so imports resolve to packages/experience-system. Configure the Registry in your app before adding recipes.

Date Range Picker

Read-only date-range picker: a formatted range display with a single calendar popover in mode=range. Click two days to commit a DateRange; serialises as YYYY-MM-DD/YYYY-MM-DD. Supports min/max/disablePast/disableFuture/disableDate constraints and configurable numberOfMonths using Field, InputGroup, Calendar, and Popover from @by/experience-system.

After shadcn add, import DateRangePicker from your registry path (for example @/components/ui/date-range-picker).

Run the shadcn CLI with your package manager. It vendors the recipe into components/ui/date-range-picker.tsx and installs the runtime dependencies (@by/experience-system, …) automatically:

pnpm dlx shadcn@latest add @by-es/date-range-picker

In this monorepo, depend on the workspace package (for example via workspace:* or your catalog) so imports resolve to packages/experience-system. Configure the Registry in your app before adding recipes.

Date & time picker

Date and time picker with segmented input (MM/DD/YYYY HH:MM), calendar popover, and 12h/24h support using Field, InputGroup, Calendar, Popover, and Button from @by/experience-system.

After shadcn add, import DateTimePicker from your registry path (for example @/components/ui/date-time-picker).

Run the shadcn CLI with your package manager. It vendors the recipe into components/ui/date-time-picker.tsx and installs the runtime dependencies (@by/experience-system, …) automatically:

pnpm dlx shadcn@latest add @by-es/date-time-picker

In this monorepo, depend on the workspace package (for example via workspace:* or your catalog) so imports resolve to packages/experience-system. Configure the Registry in your app before adding recipes.

Week Picker

Read-only week picker: a formatted week range display with a calendar popover that commits the parent week of any clicked day. Emits a DateRange { from, to }. Supports min/max/disablePast/disableFuture/disableDate constraints using Field, InputGroup, Calendar, and Popover from @by/experience-system.

Week: —

After shadcn add, import WeekPicker from your registry path (for example @/components/ui/week-picker).

Run the shadcn CLI with your package manager. It vendors the recipe into components/ui/week-picker.tsx and installs the runtime dependencies (@by/experience-system, …) automatically:

pnpm dlx shadcn@latest add @by-es/week-picker

In this monorepo, depend on the workspace package (for example via workspace:* or your catalog) so imports resolve to packages/experience-system. Configure the Registry in your app before adding recipes.

Two Month Picker

Date-range picker with two segmented inputs (start / end) sharing a single 2-month calendar popover. Built on the SegmentInput and useDateRangeSegments primitives from @by/experience-system; emits a DateRange and serialises as YYYY-MM-DD/YYYY-MM-DD. Supports min/max/disablePast/disableFuture/disableDate constraints using Field, InputGroup, Calendar, and Popover from @by/experience-system.

Range: —

After shadcn add, import TwoMonthPicker from your registry path (for example @/components/ui/two-month-picker).

Run the shadcn CLI with your package manager. It vendors the recipe into components/ui/two-month-picker.tsx and installs the runtime dependencies (@by/experience-system, …) automatically:

pnpm dlx shadcn@latest add @by-es/two-month-picker

In this monorepo, depend on the workspace package (for example via workspace:* or your catalog) so imports resolve to packages/experience-system. Configure the Registry in your app before adding recipes.

Time Picker

Time picker input with popup for selecting hours, minutes, and AM/PM using InputGroup, Popover, and Button from @by/experience-system.

Time

After shadcn add, import TimePicker from your registry path (for example @/components/ui/time-picker).

Run the shadcn CLI with your package manager. It vendors the recipe into components/ui/time-picker.tsx and installs the runtime dependencies (@by/experience-system, …) automatically:

pnpm dlx shadcn@latest add @by-es/time-picker

In this monorepo, depend on the workspace package (for example via workspace:* or your catalog) so imports resolve to packages/experience-system. Configure the Registry in your app before adding recipes.

Multi Date Range Picker

Read-only multi-date picker: an input that displays committed dates and ranges as chips, paired with a two-month calendar popover. Click days to toggle individual selections; hold Shift to select a range. Confirm/Cancel/Clear-all popover footer; emits Date[] | undefined on commit and serialises as YYYY-MM-DD,YYYY-MM-DD/YYYY-MM-DD,.... Supports min/max/disablePast/disableFuture/disableDate constraints using Badge, Button, Calendar, Field, InputGroup, Popover, and Separator from @by/experience-system.

Selection: no dates yet

After shadcn add, import MultiDateRangePicker from your registry path (for example @/components/ui/multi-date-range-picker).

Run the shadcn CLI with your package manager. It vendors the recipe into components/ui/multi-date-range-picker.tsx and installs the runtime dependencies (@by/experience-system, …) automatically:

pnpm dlx shadcn@latest add @by-es/multi-date-range-picker

In this monorepo, depend on the workspace package (for example via workspace:* or your catalog) so imports resolve to packages/experience-system. Configure the Registry in your app before adding recipes.

Date & Time Primitives

The date & time primitives are the low-level building blocks that the @by-es picker recipes above assemble into full picker fields. Use them directly when building a custom picker layout, or when the registry recipes do not fit your composition needs.

All primitives are exported from @by/experience-system and are client components ('use client'). Place them inside a Client Component or use a dynamic import when using the Next.js App Router.

import {
  SegmentInput,
  DateSegmentsInput,
  DateTimeSegmentsInput,
  PeriodSelect,
  PickerDisplayInput,
  PickerTriggerButton,
  PopupNumberInput,
  TimeSegmentsInput,
} from '@by/experience-system';

The primitives fit together into picker fields like this:

InputGroup
├── DateSegmentsInput | TimeSegmentsInput | DateTimeSegmentsInput
│     └── SegmentInput (per token) + PeriodSelect (12h mode)
├── InputGroupAddon
│     ├── InputGroupButton (clear, optional)
│     └── PickerTriggerButton (calendar / clock icon)
└── PickerDisplayInput (alternative to segment inputs — for read-only display)

PopoverContent (popup panel)
└── PopupNumberInput (hour) + PopupNumberInput (minute) + PeriodSelect (12h)

SegmentInput

A small numeric input with spinbutton semantics: arrow stepping, auto-advance, and max-length masking. It's the building block the date and time segment inputs compose on top of — use it directly only when building a fully custom segmented field.

import { SegmentInput } from '@by/experience-system';

DateSegmentsInput

No date selected

An inline row of spinbutton segments (month, day, year or similar) driven by a configurable format string. Handles keyboard navigation (ArrowLeft/Right to move, ArrowUp/Down to step, auto-advance on completion), controlled/uncontrolled value, partial/invalid state, and disabled-date rejection. The ref is forwarded to the first segment's <input> element.

import { DateSegmentsInput } from '@by/experience-system';

<DateSegmentsInput value={date} onChange={setDate} format="MM/dd/yyyy" />
PropTypeDefault
valueDate
defaultValueDate
onChange(d: Date | undefined) => void
formatMM/dd/yyyy | dd/MM/yyyy | yyyy-MM-ddMM/dd/yyyy
disabledbooleanfalse
readOnlybooleanfalse
invalidbooleanfalse
describedBystring
autoFocusbooleanfalse
requiredbooleanfalse
disabledMatcher(d: Date) => boolean
idstring
classNamestring
refReact.Ref<HTMLInputElement>

For custom layouts, use the underlying useDateSegments hook directly.

TimeSegmentsInput

No time selected

An inline row of spinbutton segments (hour, minute) plus a PeriodSelect dropdown in 12h mode. Values are canonical (24-hour hour internally). Controlled/uncontrolled, partial/invalid state. The ref is forwarded to the first segment's <input> element.

import { TimeSegmentsInput } from '@by/experience-system';

<TimeSegmentsInput value={time} onChange={setTime} format="12h" />
PropTypeDefault
valueTimeValue
defaultValueTimeValue
onChange(t: TimeValue | undefined) => void
format12h | 24h12h
disabledbooleanfalse
readOnlybooleanfalse
invalidbooleanfalse
describedBystring
autoFocusbooleanfalse
requiredbooleanfalse
idstring
classNamestring
refReact.Ref<HTMLInputElement>

TimeValue carries a 24-hour hour (0–23) and a minute (0–59). For custom layouts, use useTimeSegments directly.

DateTimeSegmentsInput

No date/time selected

An inline row of spinbutton segments for a full date-time value (month, day, year, hour, minute) plus a PeriodSelect in 12h mode. Emits a Date. The ref is forwarded to the first date segment's <input> element.

import { DateTimeSegmentsInput } from '@by/experience-system';

<DateTimeSegmentsInput value={date} onChange={setDate} dateFormat="MM/dd/yyyy" timeFormat="12h" />
PropTypeDefault
valueDate
defaultValueDate
onChange(d: Date | undefined) => void
dateFormatMM/dd/yyyy | dd/MM/yyyy | yyyy-MM-ddMM/dd/yyyy
timeFormat12h | 24h12h
disabledbooleanfalse
readOnlybooleanfalse
invalidbooleanfalse
describedBystring
autoFocusbooleanfalse
requiredbooleanfalse
disabledMatcher(d: Date) => boolean
idstring
classNamestring
refReact.Ref<HTMLInputElement>

For custom layouts, use useDateTimeSegments directly.

PeriodSelect

Selected: AM

An accessible AM / PM selector for 12-hour time fields. Renders a compact trigger (styled to sit inline inside an InputGroup) that opens a DropdownMenu. Pair with TimeSegmentsInput or useTimeSegments when building custom time fields.

import { PeriodSelect } from '@by/experience-system';

<PeriodSelect value={period} onChange={setPeriod} />
PropTypeDefault
valuePeriod ('AM' | 'PM')
onChange(value: Period) => void
disabledboolean
readOnlyboolean
invalidboolean
ariaLabelstring"Period"
describedBystring
idstring
classNamestring
refReact.Ref<HTMLButtonElement>

PickerTriggerButton

aria-expanded: false

A calendar / picker trigger button. Renders an InputGroupButton with ghost variant, icon-sm size, aria-haspopup="dialog", and aria-expanded wired to the open prop. Pass children (typically a calendar icon) to render the icon.

import { PickerTriggerButton } from '@by/experience-system';
import { CalendarIcon } from '@by/icons/ui';

<PickerTriggerButton open={open} aria-label="Open calendar">
  <CalendarIcon />
</PickerTriggerButton>

Accepts all InputGroupButton props except variant, size, and aria-haspopup (those are fixed).

PropTypeDefault
openboolean
disabledboolean
aria-labelstring
childrenReact.ReactNode
refReact.Ref<HTMLButtonElement>

PickerDisplayInput

A read-only picker display field with optional clear button and calendar trigger. Shared by DateRangePicker, WeekPicker, and similar display-only pickers. Handles InputGroup data attributes, keyboard-to-open, clear button visibility, PickerTriggerButton, and the hidden form <input>.

import { PickerDisplayInput } from '@by/experience-system';
import { CalendarIcon, XIcon } from '@by/icons/ui';

<PickerDisplayInput
  open={open}
  onOpenChange={setOpen}
  displayValue={formatted}
  serializedValue={serialized}
  placeholder="Select range"
  onClear={handleClear}
  clearIcon={<XIcon />}
  triggerIcon={<CalendarIcon />}
  clearLabel="Clear date range"
  triggerLabel="Open calendar"
/>
PropTypeDefault
openboolean
onOpenChange(open: boolean) => void
displayValuestring
serializedValuestring""
placeholderstring
disabledbooleanfalse
readOnlybooleanfalse
requiredbooleanfalse
errorbooleanfalse
autoFocusbooleanfalse
namestring
clearLabelstring"Clear"
triggerLabelstring"Open calendar"
onClear() => void
clearIconReact.ReactNode
triggerIconReact.ReactNode
triggerRefReact.Ref<HTMLButtonElement>
helperIdstring
idstring
classNamestring
refReact.Ref<HTMLInputElement>

The clear button renders only when onClear is provided, the field is not empty, and the field is interactive (not disabled or read-only). The hidden <input> (for form serialization) renders only when name is provided.

PopupNumberInput

A large-format numeric input tile for time picker popups (hour and minute). Supports arrow-up/down stepping with wrap-around, arrow-left/right for focus management, and 2-digit numeric typing. The ref is forwarded to the underlying <input> for programmatic focus.

import { PopupNumberInput } from '@by/experience-system';

<PopupNumberInput
  ref={hourRef}
  value={hourStr}
  min={1}
  max={12}
  aria-label="Hour"
  onChange={(n) => setHour(n)}
  onRightArrow={() => minuteRef.current?.focus()}
/>
PropTypeDefault
valuestring
minnumber
maxnumber
aria-labelstring
onChange(n: number) => void
onLeftArrow() => void
onRightArrow() => void
classNamestring
refReact.Ref<HTMLInputElement>

Date & Time Hooks

The date & time hooks expose the segment-driving logic behind the primitives without rendering any chrome, so you can build fully custom segmented layouts. They are exported from @by/experience-system.

useDateRangeSegments

No range selected

A hook (no component) that drives two independent date-segment groups — start and end — sharing one controlled DateRange value. Returns refs, a getSegmentProps factory keyed by RangeSide ('start' | 'end'), and derived state for validation.

import { useDateRangeSegments } from '@by/experience-system';

const { tokens, refs, getSegmentProps, startDate, endDate, isEmpty, isReversed, isInvalid } =
  useDateRangeSegments({ value: range, onChange: setRange, format: 'MM/dd/yyyy' });

Options

UseDateRangeSegmentsOptions:

OptionTypeDefault
valueDateRange | undefined
onChange(range: DateRange | undefined) => void
formatMM/dd/yyyy | dd/MM/yyyy | yyyy-MM-ddMM/dd/yyyy
disabledboolean
readOnlyboolean
invalidboolean
describedBystring
autoFocusboolean
requiredboolean
disabledMatcher(d: Date) => boolean
idstring

Return

UseDateRangeSegmentsReturn:

KeyTypeDescription
tokensDateToken[]Ordered segment keys derived from format.
separatorstringThe separator character for the format (e.g. /).
refs{ start: Record<DateToken, Ref>; end: Record<DateToken, Ref> }Input refs per side and token.
getSegmentProps(side, token, index) => SegmentInputPropsSpread-ready props for each SegmentInput.
startDateDate | undefinedParsed start — undefined when incomplete or invalid.
endDateDate | undefinedParsed end — undefined when incomplete or invalid.
startEmptybooleanAll start segments are blank.
endEmptybooleanAll end segments are blank.
isEmptybooleanBoth sides empty.
isReversedbooleanStart and end both parse but start > end.
isInvalidboolean`invalid