Skip to content

Date & time

Time

A time of day is a wall clock: no date, no time zone. 14:30 is 14:30 wherever it is read — which is why this control has no zone picker and no conversion.

Examples

Segmented field, coarse popover

The segments give exact keyboard editing; the popover gives a stepped grid. Both write the same Time value, and the step only ever constrains the grid — typing 14:07 is still allowed.

1430

24-hour, 15-minute grid.

094530

granularity="second", 5-second grid.

930AM

No hourCycle — the locale decides (this is 12-hour for en-US).

canonical values: 14:30:00, 09:45:30 · rendered as 14:30

A bounded window

`minValue` and `maxValue` bound the popover and validate the typed segments. They are a constraint on the value, not a substitution for one: the control never nudges an out-of-range entry into range behind the user's back.

1430

Opens at 09:30 and closes at 16:00.

1100AM

The same bound, twelve-hour display.

Clocks are not time zones

If a product needs “14:30 in Berlin”, that is a zoned value — `DateTimePicker` or `ZonedDateTimePicker`. A time-only control that silently attached a zone would be lying about what it stores.

Time (this control)

14:30 — a wall clock. No zone, no DST, no date.

ZonedDateTime

2026-09-18T14:30[Europe/Berlin] — an instant with a rule attached.

This machine

The zone is read in the browser once the page is interactive.

States

The same shell and the same states as every other control: hover steps one border, focus draws the ring, invalid is negative and points at its message, disabled leaves the tab order.

––––

Empty

0930

Filled

1800

Invalid

––––

Disabled

0930

Read-only

0930

Small

Usage

import { Time, TimePicker } from "@luc/ui";
<TimePicker aria-label="Session close" value={time} onValueChange={setTime} hourCycle={24} />

The value is a `Time` — hours, minutes and optionally seconds with no zone. `hourCycle` is the only display decision; leave it off and the locale decides, which is what a product with more than one region should do.

API

Read from the published TypeScript API, so a prop that is not listed here does not exist and a default that is listed is the one the compiler sees.

Exported symbols
ExportKindSignature
TimePicker60 documented props({ onValueChange, size, invalid, disabled, readOnly, locale, minuteStep, granularity, secondStep, showNowAction, className, value, ...props }: TimePickerProps) => import("@types/react").JSX.Element

TimePicker props

Type: TimePickerProps

Values the compiler reads from the declaration.

TimePicker props
PropTypeDefaultNotes
aria-describedby?inheritedstring | undefined—Identifies the element (or elements) that describes the object.
aria-details?inheritedstring | undefined—Identifies the element (or elements) that provide a detailed, extended description for the object.
aria-label?string | undefined—Defines a string value that labels the current element.
aria-labelledby?inheritedstring | undefined—Identifies the element (or elements) that labels the current element.
autoFocus?inheritedboolean | undefined—Whether the element should receive focus on render.
className?string | undefined—
defaultValue?inheritedTime | null | undefined—The default value (uncontrolled).
dir?inheritedstring | undefined—
disabled?boolean | undefined—
form?inheritedstring | undefined—The `<form>` element to associate the input with. The value of this attribute must be the id of a `<form>` in the same document. See [MDN](https://developer.mozilla.org/en-US/docs…
granularity?"minute" | "second" | undefined—Also pick seconds; the field then shows a seconds segment.
hidden?inheritedboolean | undefined—
hideTimeZone?inheritedboolean | undefined—Whether to hide the time zone abbreviation.
hourCycle?inherited24 | 12 | undefined—Whether to display the time in 12 or 24 hour format. By default, this is determined by the user's locale.
id?inheritedstring | undefined—The element's unique identifier. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id).
inert?inheritedboolean | undefined—
invalid?boolean | undefined—
isRequired?inheritedboolean | undefined—Whether user input is required on the input before form submission.
lang?inheritedstring | undefined—
locale?string | undefined—
maxValue?inheritedTimeValue | null | undefined—The maximum allowed time that a user may select.
minuteStep?number | undefined—Minute granularity in the coarse picker. Default `5`.
minValue?inheritedTimeValue | null | undefined—The minimum allowed time that a user may select.
name?inheritedstring | undefined—The name of the input element, used when submitting an HTML form. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdefname).
onAnimationEnd?inheritedAnimationEventHandler<HTMLDivElement> | undefined—
onAnimationEndCapture?inheritedAnimationEventHandler<HTMLDivElement> | undefined—
onAnimationIteration?inheritedAnimationEventHandler<HTMLDivElement> | undefined—
onAnimationIterationCapture?inheritedAnimationEventHandler<HTMLDivElement> | undefined—
onAnimationStart?inheritedAnimationEventHandler<HTMLDivElement> | undefined—
onAnimationStartCapture?inheritedAnimationEventHandler<HTMLDivElement> | undefined—
onAuxClick?inheritedMouseEventHandler<HTMLDivElement> | undefined—
onAuxClickCapture?inheritedMouseEventHandler<HTMLDivElement> | undefined—
onBlur?inherited((e: FocusEvent<Element, Element>) => void) | undefined—Handler that is called when the element loses focus.
onChange?inherited((value: Time | null) => void) | undefined—Handler that is called when the value changes.
onClick?inheritedMouseEventHandler<HTMLDivElement> | undefined—
onClickCapture?inheritedMouseEventHandler<HTMLDivElement> | undefined—
onContextMenu?inheritedMouseEventHandler<HTMLDivElement> | undefined—
onContextMenuCapture?inheritedMouseEventHandler<HTMLDivElement> | undefined—
onDoubleClick?inheritedMouseEventHandler<HTMLDivElement> | undefined—
onDoubleClickCapture?inheritedMouseEventHandler<HTMLDivElement> | undefined—
onFocus?inherited((e: FocusEvent<Element, Element>) => void) | undefined—Handler that is called when the element receives focus.
onFocusChange?inherited((isFocused: boolean) => void) | undefined—Handler that is called when the element's focus status changes.
onGotPointerCapture?inheritedPointerEventHandler<HTMLDivElement> | undefined—
onGotPointerCaptureCapture?inheritedPointerEventHandler<HTMLDivElement> | undefined—
onKeyDown?inherited((e: KeyboardEvent) => void) | undefined—Handler that is called when a key is pressed.
onKeyUp?inherited((e: KeyboardEvent) => void) | undefined—Handler that is called when a key is released.
onLostPointerCapture?inheritedPointerEventHandler<HTMLDivElement> | undefined—
onLostPointerCaptureCapture?inheritedPointerEventHandler<HTMLDivElement> | undefined—
onMouseDown?inheritedMouseEventHandler<HTMLDivElement> | undefined—
onMouseDownCapture?inheritedMouseEventHandler<HTMLDivElement> | undefined—
onMouseEnter?inheritedMouseEventHandler<HTMLDivElement> | undefined—
onMouseLeave?inheritedMouseEventHandler<HTMLDivElement> | undefined—
onMouseMove?inheritedMouseEventHandler<HTMLDivElement> | undefined—
onMouseMoveCapture?inheritedMouseEventHandler<HTMLDivElement> | undefined—
onMouseOut?inheritedMouseEventHandler<HTMLDivElement> | undefined—
onMouseOutCapture?inheritedMouseEventHandler<HTMLDivElement> | undefined—
onMouseOver?inheritedMouseEventHandler<HTMLDivElement> | undefined—
onMouseOverCapture?inheritedMouseEventHandler<HTMLDivElement> | undefined—
onMouseUp?inheritedMouseEventHandler<HTMLDivElement> | undefined—
onMouseUpCapture?inheritedMouseEventHandler<HTMLDivElement> | undefined—

Guidelines

Do

  • Leave `hourCycle` unset and let the locale decide, unless the product has one house convention; a 12-hour and a 24-hour clock are two different readers, not two qualities.
  • Match the popover to the value with `minuteStep` (and `secondStep` when `granularity="second"`), so the coarse grid offers the times the field can actually represent.
  • Bound a session with `minValue`/`maxValue` when the domain has opening hours, and let the field report the violation instead of correcting it silently.

Don't

  • Store a time of day as a full timestamp. A wall clock with a date attached invites a zone question the value cannot answer.
  • Attach a zone to a `Time`: “14:30 in Berlin” is a `ZonedDateTime`, and `ZonedDateTimePicker` is the control for it.
  • Treat the step as validation — typing 14:07 is allowed on a five-minute grid, because the step constrains the popover rather than the value.

Accessibility

The field is one group with labelled hour, minute and (when asked for) second segments: a single tab stop, arrow keys between segments, Up and Down to step a segment, and digits typed straight in. The popover's columns are listboxes with roving focus, and the Now action is a real button rather than a click target on the value.

Luc UI v1.0TimePickerdark theme