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.
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 }} />
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.
<TimePicker ampm={false} />
<TimePicker locale="de-DE" />
Modal dialog
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'.
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.
<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
| Prop | Type | Default | Description |
|---|---|---|---|
amLabel | string | AM | |
ampm | boolean | — | 12-hour clock with AM / PM (`true`) or 24-hour clock (`false`). Defaults to the `locale`'s hour cycle. |
cancelLabel | string | Cancel | |
defaultMode | enum | 'dial' | Uncontrolled initial display mode. |
defaultValue | TimeValue | { 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. |
hourLabel | string | Hour | Name of the hour input / dial and its supporting text. |
locale | string | en-US | Locale whose hour cycle sets the default `ampm`. |
minuteLabel | string | Minute | Name of the minute input / dial and its supporting text. |
mode | enum | — | 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. |
okLabel | string | OK | |
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. |
open | boolean | — | Show the picker as a modal dialog (scrim, focus trap, Escape) — the Compose `TimePickerDialog` equivalent. Renders nothing while `false`. |
periodLabel | string | AM or PM | Name of the AM / PM group. |
pmLabel | string | PM | |
selectHourLabel | string | Select hour | Name of the hour selector. |
selectMinuteLabel | string | Select minutes | Name of the minute selector. |
showModeToggle | boolean | true | Show the dial / input toggle. |
switchToDialLabel | string | Toggle dial picker | Name of the toggle while showing the text input. |
switchToInputLabel | string | Toggle input picker | Name of the toggle while showing the dial. |
titleLabel | string | 'Select time' ('Enter time' in input mode) | Headline. |
value | TimeValue | — | Controlled value. |