VisorVisor
ComponentsForm

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-picker

This copies two files into your project:

  • components/ui/time-picker/time-picker.tsx — the component
  • components/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

PropTypeDefaultDescription
valuestring—Selected time as 24-hour "HH:MM".
defaultValuestring—Initial "HH:MM" when uncontrolled.
onChange(value: string | undefined) => void—Called with 24-hour "HH:MM", or undefined when the field is cleared.
hourCycle12 | 24from locale12 shows "4:30 PM" and adds an AM/PM column; 24 shows "16:30".
localestringdocument languageBCP 47 locale that decides the default hour cycle.
minuteStepnumber1Minutes between popover choices. Typed times snap to the nearest step.
placeholderstring—Text shown when no time is selected.
disabledbooleanfalseDisables the field and the clock button.
openboolean—Controlled popover state.
onOpenChange(open: boolean) => void—Called when the popover opens or closes.
containerHTMLElement | null—Element the popover portals into. Defaults to document.body.
triggerLabelstring'Choose time'Accessible name of the clock button.

Accessibility

  • The field is a labelled text input: pass aria-label, aria-labelledby or an associated <Label htmlFor> with id
  • The clock button has its own accessible name (triggerLabel)
  • Each popover column is a listbox with aria-selected options; 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