VayuUI

Timepicker

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

Usage

Basic (12h format)

24-Hour Format

Time Range

With Constraints (9AM - 5PM)

import { Timepicker, TimeValue } from "vayu-ui";
import { useState } from "react";

function Example() {
  const [time, setTime] = useState<TimeValue | null>(null);

  return (
    <Timepicker.Root
      value={time}
      onValueChange={setTime}
      placeholder="Select time"
    >
      <Timepicker.Trigger />
      <Timepicker.Content>
        <Timepicker.TimeGrid />
        <Timepicker.Footer />
      </Timepicker.Content>
    </Timepicker.Root>
  );
}

Features

  • 12h and 24h formats - Toggle between AM/PM and 24-hour display
  • Time range selection - Select start and end times with mode="range"
  • Disabled times - Disable specific hours, time ranges, or individual times
  • Min/Max bounds - Restrict selection to a time window (e.g., business hours)
  • Custom minute steps - Configure minute intervals (5, 10, 15, 30 minutes)
  • React Hook Form ready - Works with Controller for form integration
  • WCAG 2.2 AA accessible - Full keyboard navigation and screen reader support
  • Compound component pattern - Flexible and composable API

Compound Components

ComponentDescription
Timepicker.RootContext provider that manages state and configuration
Timepicker.TriggerButton that displays selected time and opens dropdown
Timepicker.ContentPortal-rendered dropdown containing time columns
Timepicker.TimeGridPre-configured layout of Hour, Minute, and Period columns
Timepicker.HourColumnIndividual hour selection column
Timepicker.MinuteColumnIndividual minute selection column
Timepicker.PeriodColumnAM/PM toggle (only shown in 12h format)
Timepicker.FooterAction buttons (Clear, Apply)

Examples

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

import { Timepicker, TimeRange } from 'vayu-ui';

function RangeExample() {
  const [range, setRange] = useState<TimeRange | null>(null);

  const handleRangeChange = (value: TimeValue | TimeRange | null) => {
    setRange(value as TimeRange | null);
  };

  return (
    <Timepicker.Root
      value={range}
      onValueChange={handleRangeChange}
      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>

React Hook Form Integration

import { Controller, useForm } from 'react-hook-form';
import { Timepicker, TimeValue } from 'vayu-ui';

interface FormData {
  appointmentTime: TimeValue | null;
}

function FormExample() {
  const { control, handleSubmit } = useForm<FormData>();

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Controller
        name="appointmentTime"
        control={control}
        rules={{ required: 'Time is required' }}
        render={({ field, fieldState }) => (
          <Timepicker.Root
            value={field.value}
            onValueChange={field.onChange}
            error={fieldState.error?.message}
            label="Appointment Time"
          >
            <Timepicker.Trigger />
            <Timepicker.Content>
              <Timepicker.TimeGrid />
              <Timepicker.Footer />
            </Timepicker.Content>
          </Timepicker.Root>
        )}
      />
      <button type="submit">Submit</button>
    </form>
  );
}

Props

Timepicker.Root

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

TimeValue Type

interface TimeValue {
  hour: number; // 0-23
  minute: number; // 0-59
}

TimeRange Type

interface TimeRange {
  start: TimeValue | null;
  end: TimeValue | null;
}

Accessibility

The Timepicker component follows WCAG 2.2 AA guidelines:

  • Keyboard Navigation

    • Tab - Move focus between trigger and time columns
    • Arrow Up/Down - Navigate within time columns
    • Enter/Space - Select focused time item
    • Escape - Close dropdown without applying
  • Screen Reader Support

    • Proper ARIA roles (listbox, option, combobox)
    • aria-selected and aria-disabled states
    • Descriptive labels for all interactive elements
  • Focus Management

    • Visible focus indicators on all focusable elements
    • Focus returns to trigger when dropdown closes
    • Focus ring uses semantic --color-focus token

Styling

The Timepicker uses semantic design tokens from the theme:

TokenUsage
bg-surfaceTrigger background
bg-elevatedDropdown content background
bg-brandSelected item background
border-fieldDefault border color
border-focusFocus border color
rounded-controlTrigger border radius
rounded-overlayDropdown border radius
shadow-elevatedDropdown shadow

On this page