Time Picker
A time picker with a typeable field and a popover of hour and minute columns, in a 12- or 24-hour cycle with a configurable minute step.
Default
The hour cycle comes from the locale. The value in and out is always a 24-hour "HH:MM" string.
12-hour
24-hour
An hour column and a minute column (00, 15, 30, 45).
Disabled
Installation
npx visor add time-pickerThis copies two files into your project:
components/ui/time-picker/time-picker.tsx— the componentcomponents/ui/time-picker/time-picker.module.css— the styles
Usage
import { TimePicker } from '@/components/ui/time-picker/time-picker';
import { useState } from 'react';
export default function Example() {
const [opensAt, setOpensAt] = useState<string>();
return (
<TimePicker
value={opensAt}
onChange={setOpensAt}
minuteStep={15}
aria-label="Doors open"
/>
);
}Typing
The field is a real text input, so a time can be typed without opening the popover. It accepts 16:30, 1630, 4:30pm, 4 pm and 4p. In the 12-hour cycle a bare hour from 1 to 12 keeps the current AM or PM. The value commits on Enter or blur, snapped to the nearest minute step; text that does not parse reverts to the last good value. Arrow Up and Arrow Down step the time by minuteStep, and Alt + Arrow Down opens the popover.
Hour cycle
hourCycle is 12 or 24. When omitted it is read from locale, then from the document's lang, then from the runtime default. Set it explicitly where server and browser locales can differ.
Inside a field control
The field draws its edge from the shared --control-edge-* tokens and matches Input at the small size, so it shares a baseline with sibling inputs in the same row.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Selected time as 24-hour "HH:MM". |
defaultValue | string | — | Initial "HH:MM" when uncontrolled. |
onChange | (value: string | undefined) => void | — | Called with 24-hour "HH:MM", or undefined when the field is cleared. |
hourCycle | 12 | 24 | from locale | 12 shows "4:30 PM" and adds an AM/PM column; 24 shows "16:30". |
locale | string | document language | BCP 47 locale that decides the default hour cycle. |
minuteStep | number | 1 | Minutes between popover choices. Typed times snap to the nearest step. |
placeholder | string | — | Text shown when no time is selected. |
disabled | boolean | false | Disables the field and the clock button. |
open | boolean | — | Controlled popover state. |
onOpenChange | (open: boolean) => void | — | Called when the popover opens or closes. |
container | HTMLElement | null | — | Element the popover portals into. Defaults to document.body. |
triggerLabel | string | 'Choose time' | Accessible name of the clock button. |
Accessibility
- The field is a labelled text input: pass
aria-label,aria-labelledbyor an associated<Label htmlFor>withid - The clock button has its own accessible name (
triggerLabel) - Each popover column is a listbox with
aria-selectedoptions; arrow keys, Home and End move within a column, and focus lands on the selected hour when the popover opens - Escape closes the popover and returns focus to the clock button