VayuUI

Timepicker

A flexible time picker with 12/24 hour formats, time range selection, and disabled time constraints.

Installation

npx vayu-ui-cli@latest add timepicker

Usage

Basic (12h format)

24-Hour Format

Time Range

With Constraints (9AM - 5PM)

{/* Single Select - 12h */}
<Timepicker.Root value={time} onValueChange={setTime} placeholder="Select time">
  <Timepicker.Trigger />
  <Timepicker.Content>
    <Timepicker.TimeGrid />
    <Timepicker.Footer />
  </Timepicker.Content>
</Timepicker.Root>

{/* 24-Hour Format */}
<Timepicker.Root format="24h" placeholder="Select time">
  <Timepicker.Trigger />
  <Timepicker.Content>
    <Timepicker.TimeGrid />
    <Timepicker.Footer />
  </Timepicker.Content>
</Timepicker.Root>

{/* Time Range Selection */}
<Timepicker.Root mode="range" showApplyButton placeholder="Select time range">
  <Timepicker.Trigger />
  <Timepicker.Content>
    <Timepicker.TimeGrid />
    <Timepicker.Footer />
  </Timepicker.Content>
</Timepicker.Root>

{/* Disabled Times */}
<Timepicker.Root
  disabledHours={[0, 1, 2, 3, 4, 5, 6, 7, 20, 21, 22, 23]}
  disabledTimes={['12:00', '12:30', '13:00']}
  placeholder="Business hours only"
>
  <Timepicker.Trigger />
  <Timepicker.Content>
    <Timepicker.TimeGrid />
    <Timepicker.Footer />
  </Timepicker.Content>
</Timepicker.Root>

{/* Min/Max Time Bounds */}
<Timepicker.Root minTime="09:00" maxTime="17:00" placeholder="Work hours (9AM - 5PM)">
  <Timepicker.Trigger />
  <Timepicker.Content>
    <Timepicker.TimeGrid />
    <Timepicker.Footer />
  </Timepicker.Content>
</Timepicker.Root>

{/* Custom Minute Step */}
<Timepicker.Root minuteStep={15} placeholder="Select time (15-min intervals)">
  <Timepicker.Trigger />
  <Timepicker.Content>
    <Timepicker.TimeGrid />
    <Timepicker.Footer />
  </Timepicker.Content>
</Timepicker.Root>

{/* Error State */}
<Timepicker.Root label="Appointment Time" error="Time is required">
  <Timepicker.Trigger />
  <Timepicker.Content>
    <Timepicker.TimeGrid />
    <Timepicker.Footer />
  </Timepicker.Content>
</Timepicker.Root>

Anatomy

import { Timepicker } from 'vayu-ui';

<Timepicker.Root value={time} onValueChange={setTime} format="12h" label="Start time">
  <Timepicker.Trigger />
  <Timepicker.Content>
    <Timepicker.TimeGrid />
    <Timepicker.Footer />
  </Timepicker.Content>
</Timepicker.Root>;

{
  /* Custom column layout */
}
<Timepicker.Root format="24h">
  <Timepicker.Trigger />
  <Timepicker.Content>
    <Timepicker.HourColumn />
    <Timepicker.MinuteColumn />
    <Timepicker.Error />
    <Timepicker.Footer />
  </Timepicker.Content>
</Timepicker.Root>;
  • Timepicker.Root — Root container. Manages time state, format, mode, and provides context to children.
  • Timepicker.Trigger — Button with inline editing. Displays the selected time and opens the dropdown.
  • Timepicker.Content — Portal-based dropdown with focus trap and auto-positioning.
  • Timepicker.TimeGrid — Pre-configured layout of hour, minute, and period columns.
  • Timepicker.HourColumn — Individual hour selection column with scroll and keyboard navigation.
  • Timepicker.MinuteColumn — Individual minute selection column, respects minuteStep.
  • Timepicker.PeriodColumn — AM/PM toggle column, only rendered in 12h format.
  • Timepicker.Footer — Action buttons for clearing and applying selection.
  • Timepicker.Error — Error message display within the dropdown.

Accessibility

  • Keyboard Support:
    • Enter / Space — Select the focused time item or open the dropdown.
    • ArrowDown / ArrowUp — Navigate within a time column (wraps circularly).
    • ArrowLeft / ArrowRight — Navigate between columns (hour, minute, period).
    • Home / End — Jump to first / last item in the current column.
    • Escape — Close the dropdown and return focus to the trigger.
    • Tab — Move focus through the dropdown content (trapped while open). Shift+Tab reverses.
    • Backspace — In trigger inline mode, decrement the focused field value.
  • ARIA Attributes:
    • role="combobox" and aria-expanded on Timepicker.Trigger.
    • role="dialog" and aria-modal="true" on Timepicker.Content.
    • role="listbox" on each time column with aria-label ("Hour", "Minute", "Period").
    • role="option" with aria-selected and aria-disabled on each time item.
    • aria-labelledby links the label to the trigger for identification.
  • Focus Behavior:
    • Opening the dropdown focuses the first selected option, or the first enabled item.
    • Closing via Escape returns focus to the trigger.
    • Focus is trapped within the dropdown while open.
    • Selected items auto-scroll into view when the dropdown opens.

Screen reader behavior

When a user focuses the trigger, the screen reader announces the label, the currently selected time or placeholder, and the expanded state via aria-expanded. When the dropdown opens, the dialog role is announced along with the column listboxes. Each time option is announced with its value, selected state via aria-selected, and disabled state via aria-disabled. Navigating between columns with arrow keys announces the column label. In range mode, the current phase (start or end) is announced when switching. Closing the dropdown returns focus to the trigger, which announces the updated time value.

Component Folder Structure

TimePicker/
├── TimePicker.tsx           # Root component with context provider and state management
├── TimePickerTrigger.tsx    # Trigger with inline editing and keyboard navigation
├── TimePickerContent.tsx    # Portal-based dropdown with focus trap and positioning
├── TimePickerColumns.tsx    # Hour, Minute, Period columns with grid layout
├── TimePickerFooter.tsx     # Footer actions (Clear, Apply) and error display
├── utils.ts                 # Time parsing, formatting, validation utilities
├── types.ts                 # TypeScript type definitions
├── index.ts                 # Re-exports all components and types
└── README.md                # Component usage reference

Props

Timepicker.Root

PropTypeDefaultDescription
childrenReactNodeTimepicker subcomponents.
valueTimeValue | TimeRange | nullControlled value.
defaultValueTimeValue | TimeRange | nullUncontrolled initial value.
onValueChange(value: TimeValue | TimeRange | null) => voidCallback when value changes.
format'12h' | '24h''12h'Time display format.
mode'single' | 'range''single'Single time or time range selection.
labelstringAccessible label for the timepicker.
errorstringError message to display.
disabledbooleanfalseDisable the timepicker.
placeholderstring'Select time'Placeholder text in the trigger.
minuteStepnumber5Minute increment step.
clearablebooleantrueShow clear button in footer.
showApplyButtonbooleanfalseShow Apply button in footer.
minTimestringMinimum selectable time in HH:MM format.
maxTimestringMaximum selectable time in HH:MM format.
disabledTimesstring[]Array of disabled times in HH:MM format.
disabledHoursnumber[]Array of disabled hours (0-23).
classNamestringAdditional CSS classes.

Timepicker.Trigger

PropTypeDefaultDescription
classNamestringAdditional CSS classes.

Timepicker.Content

PropTypeDefaultDescription
childrenReactNodeDropdown content subcomponents.
classNamestringAdditional CSS classes.

Timepicker.TimeGrid

PropTypeDefaultDescription
classNamestringAdditional CSS classes.

Timepicker.HourColumn

PropTypeDefaultDescription
classNamestringAdditional CSS classes.

Timepicker.MinuteColumn

PropTypeDefaultDescription
classNamestringAdditional CSS classes.

Timepicker.PeriodColumn

PropTypeDefaultDescription
classNamestringAdditional CSS classes.

Timepicker.Footer

PropTypeDefaultDescription
childrenReactNodeCustom footer content.
classNamestringAdditional CSS classes.

Timepicker.Error

PropTypeDefaultDescription
childrenReactNodeCustom error content.
classNamestringAdditional CSS classes.

On this page