Skip to main content

TimePicker

betav0.1.0

Time-of-day field for forms: an editable text field wearing the Input shell (same label, required marker, helper and error rows, same hover/focus/disabled/error states), a clock button in its end slot, and a popover holding the clock row DatePicker's withTime already ships — hours, minutes, seconds when the pattern carries `ss`, AM/PM when it carries `a` — plus an OK button. The value is a Date whose clock is the field's and whose calendar day is the value's own (else today), or null; typing is parsed against a locale-derived or explicit `format` on every keystroke and reported with the same MUI X shaped validation context as DatePicker. Covers MUI X TimePicker and the native `<input type="time">`. Same engine as DatePicker, no date dependency.

Variants

Type a time or use the clock
12-hour locale, 15-minute steps
Seconds and AM/PM: two lines of two
A time is required

Examples

<TimePicker label="Time" value={time} onChange={setTime} />
<TimePicker
  label="Time of day"
  value={time}
  onChange={(next, { validationError }) => { if (!validationError) setTime(next); }}
  onAccept={submit}
  helperText="Digest sent every day at this time"
/>
// A 15-minute stepper and an earliest time, MUI X's timeSteps and minTime
<TimePicker label="Scheduling time" minutesStep={15} minTime={new Date()} value={time} onChange={setTime} />
<TimePicker label="Time (UTC)" timezone="UTC" format="HH:mm" value={time} onChange={setTime} />
<TimePicker label="Time" locale="en-US" format="hh:mm:ss a" clearable value={time} onChange={setTime} />
<TimePicker label="Time" error="A time is required" required value={null} onChange={setTime} />
<TimePicker label="Time" disabled value={new Date(2026, 8, 26, 9, 30)} />
<TimePicker aria-label="Start" value={start} onChange={setStart} />

Props

PropType
valueDate | null — Controlled value; the clock is the field's, the calendar day is left alone (RULE-02)
defaultValueDate | null — Uncontrolled initial value
onChange(value: Date | null, context: { validationError: null | 'invalidDate' | 'minTime' | 'maxTime' }) => void — Fires on every clock edit, keystroke and clear; unparsable text reports null with 'invalidDate', a time outside the bounds reports it with the bound (RULE-04)
onAccept(value: Date | null) => void — Fires when the user commits: OK, Enter, blur after typing, clear (RULE-05)
formatstring — Clock pattern with the tokens HH hh mm ss a; anything else is a literal. Defaults to the locale's clock, HH:mm or hh:mm a (RULE-06)
localestring — BCP 47 tag for the default pattern and the AM/PM strings. Defaults to the host locale (RULE-06)
timezonestring — The zone the clock is read in: "default" (the browser's), "UTC" or an IANA name such as "Europe/Paris". The value stays an instant; an unknown name warns and falls back to the browser (RULE-08)
minTimeDate — Earliest selectable time of day, inclusive; the bound's calendar day is ignored
maxTimeDate — Latest selectable time of day, inclusive
minutesStepnumber — Increment of the minutes stepper and its ArrowUp/ArrowDown (MUI X timeSteps.minutes; default: 1)
clearableboolean — Shows a clear button next to the clock button while the field holds a value (default: false, RULE-07)
openboolean — Controlled open state of the clock panel
defaultOpenboolean — Uncontrolled initial open state (default: false)
onOpenChange(open: boolean) => void — Open-state change callback
labelstring — Visible label above the field. Omit → aria-label or aria-labelledby required (RULE-01)
requiredboolean — Renders the `*` marker (with a label) and sets aria-required (default: false)
infoTooltipReactNode — Info affordance next to the label (caller-owned); renders only with a label
placeholderstring — Placeholder shown when empty. Defaults to the pattern
helperTextReactNode — Helper text under the field; replaced by `error`
errorstring — Error message. String only (no boolean) — sets aria-invalid and the error tokens (RULE-05)
startIconReactNode — Single leading decorative icon, always aria-hidden
disabledboolean — Disables the field, both buttons and the panel (default: false)
readOnlyboolean — Text not editable, clock button disabled (default: false)
openClockLabelstring — Accessible name of the clock button (default: "Open clock")
clearLabelstring — Accessible name of the clear button (default: "Clear time")
clockLabelstring — Accessible name of the panel (default: "<label> clock" or "Clock")
hoursLabelstring — Label of the hours field (default: "Hours")
minutesLabelstring — Label of the minutes field (default: "Minutes")
secondsLabelstring — Label of the seconds field (default: "Seconds")
periodLabelstring — Accessible name of the AM/PM select (default: "AM/PM")
confirmLabelstring — Label of the OK button (default: "OK")
classNamestring — Additional CSS classes on the outer wrapper

Accessibility

2.1 AAWCAG pass

The field is a native text <input> wearing Input's classes (inputVariants), so its label, required marker, helper/error rows, aria-required, aria-invalid and aria-describedby wiring are Input's; omitting label requires aria-label or aria-labelledby, enforced by a dev-only console.warn like Input (RULE-01). The clock button is an FDS IconButton acting as the Radix Popover trigger (aria-expanded, aria-haspopup=dialog); the panel is a modal dialog named after the label, and focus lands on the hours field on open. The hours, minutes and seconds fields are FDS Inputs with visible labels and the native spinbutton semantics (ArrowUp/ArrowDown step them, the minutes by `minutesStep`); AM/PM is an FDS Select; OK is an FDS Button; Tab cycles inside the dialog and Escape returns focus to the clock button. Every rendered string is a prop with an English default so products localise. An invalid typed value is reported to the consumer, who renders the message through `error` (RULE-05). The field's disabled tones follow Input's declared pairs. Focus uses the standard FDS ring on the input, the buttons, the select and the clock fields.