Skip to main content

TimePicker

MD3 time picker with a clock dial and a keyboard input mode, shown inline or as a modal dialog, in 12-hour (AM/PM) or 24-hour format.

Dial​

The value is a TimeValue — { hour, minute } with hour always in 24-hour form (0–23). onChange(value) is value-first. Use value for a controlled picker or defaultValue (default { hour: 12, minute: 0 }) for an uncontrolled one. Picking an hour on the dial with a pointer switches to minutes.

9 o'clock30 minutes

Selected: 09:30

const [time, setTime] = useState<TimeValue>({ hour: 9, minute: 30 })

<TimePicker value={time} onChange={setTime} />

Input mode​

The keyboard / clock toggle switches between the dial and text input. Start in input mode with defaultMode="input", control it with mode + onModeChange, or hide the toggle with showModeToggle={false}. Input is validated (two digits, in range): invalid text shows an error and is never reported through onChange.

<TimePicker defaultMode="input" defaultValue={{ hour: 14, minute: 15 }} />
note

Passing mode without onModeChange is deprecated: it only sets the initial mode (v1.0 behavior). Use defaultMode for that; in v2 mode will always be controlled.

24-hour clock​

ampm={false} shows a 24-hour dial (an inner 12–23 ring) without AM/PM. When ampm is omitted it follows the locale's hour cycle (default 'en-US', 12-hour), so locale="ja-JP" or "de-DE" gives a 24-hour clock.

18 hours45 minutes
<TimePicker ampm={false} />
<TimePicker locale="de-DE" />

Pass open to show the picker as a modal dialog (scrim, focus trap, Escape) — the equivalent of Compose's TimePickerDialog; it renders nothing while false. It adds the "Select time" headline and the Cancel / OK row with draft / commit semantics: onChange reports the draft, OK (or Enter in the minute field) calls onAccept, and Cancel, Escape, or a scrim click reverts the value and calls onCancel. onClose(reason) asks you to close, with 'accept' | 'cancel' | 'escapeKeyDown' | 'backdropClick'.

Alarm: —
const [open, setOpen] = useState(false)
const [draft, setDraft] = useState<TimeValue>({ hour: 8, minute: 0 })

<Button onClick={() => setOpen(true)}>Set alarm</Button>
<TimePicker
open={open}
onClose={(reason) => setOpen(false)}
value={draft}
onChange={setDraft}
onAccept={(time) => saveAlarm(time)}
/>

Passing onAccept or onCancel without open renders the headline and action row on an inline picker.

Localization​

Every built-in string is a prop with an English default: titleLabel, hourLabel, minuteLabel, amLabel, pmLabel, okLabel, cancelLabel, switchToInputLabel, switchToDialLabel, and the getHourLabel / getMinuteLabel / getErrorLabel functions.

時刻を選択
7時20分
<TimePicker
locale="ja-JP"
titleLabel="時刻を選択"
cancelLabel="キャンセル"
getHourLabel={(hour) => `${hour}時`}
getMinuteLabel={(minute) => `${minute}分`}
/>

Accessibility​

  • The hour / minute selectors and AM / PM are radio groups (one Tab stop each; arrow keys move).
  • The dial is a single Tab stop on the selected number: arrow keys move around the ring (continuing into the inner ring in 24-hour mode) and Enter / Space select. Numbers are read as "3 o'clock" / "15 minutes" (customize with getHourLabel / getMinuteLabel).
  • The modal picker traps focus, closes on Escape, and returns focus to the opener.

Props​

PropTypeDefaultDescription
amLabelstringAM
ampmboolean—12-hour clock with AM / PM (`true`) or 24-hour clock (`false`). Defaults to the `locale`'s hour cycle.
cancelLabelstringCancel
defaultModeenum'dial'Uncontrolled initial display mode.
defaultValueTimeValue{ hour: 12, minute: 0 }Uncontrolled initial value.
getErrorLabel((field: "hour" | "minute", ampm: boolean) => string)(field: Field, ampm: boolean) => field === 'minute' ? 'Minute must be 0–59' : ampm ? 'Hour must be 1–12' : 'Hour must be 0–23'Input-mode error for an out-of-range field.
getHourLabel((hour: number, ampm: boolean) => string)(hour: number, ampm: boolean) => ampm ? `${hour} o'clock` : `${hour} hours`Accessible name of an hour on the dial and of the hour selector's value. Receives the displayed hour (1–12 with AM/PM, 0–23 without).
getMinuteLabel((minute: number) => string)(minute: number) => `${minute} minutes`Accessible name of a minute on the dial and of the minute selector's value.
hourLabelstringHourName of the hour input / dial and its supporting text.
localestringen-USLocale whose hour cycle sets the default `ampm`.
minuteLabelstringMinuteName of the minute input / dial and its supporting text.
modeenum—Display mode: clock `dial` or keyboard `input`. Controlled when `onModeChange` is also passed. @deprecated Passing `mode` **without** `onModeChange` keeps the v1.0 behavior — it only sets the initial mode and the toggle still switches. Use `defaultMode` for that; in v2 `mode` will always be controlled.
okLabelstringOK
onAccept((value: TimeValue) => void)—OK (or Enter in the minute field) commits the draft. Passing `onAccept`, `onCancel` or `open` adds the headline and the Cancel / OK row.
onCancel(() => void)—Cancel, Escape or a scrim click: the value reverts (through `onChange`) to what it was when the picker opened or was last accepted.
onChange((value: TimeValue) => void)—Fires with the new value on every change — with the action row this is the draft; `onAccept` commits.
onClose((reason: TimePickerCloseReason) => void)—A modal picker asks to close, with the reason.
onModeChange((mode: TimePickerMode) => void)—Fires when the toggle switches the display mode.
openboolean—Show the picker as a modal dialog (scrim, focus trap, Escape) — the Compose `TimePickerDialog` equivalent. Renders nothing while `false`.
periodLabelstringAM or PMName of the AM / PM group.
pmLabelstringPM
selectHourLabelstringSelect hourName of the hour selector.
selectMinuteLabelstringSelect minutesName of the minute selector.
showModeTogglebooleantrueShow the dial / input toggle.
switchToDialLabelstringToggle dial pickerName of the toggle while showing the text input.
switchToInputLabelstringToggle input pickerName of the toggle while showing the dial.
titleLabelstring'Select time' ('Enter time' in input mode)Headline.
valueTimeValue—Controlled value.