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
Select 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
| Component | Description |
|---|---|
Timepicker.Root | Context provider that manages state and configuration |
Timepicker.Trigger | Button that displays selected time and opens dropdown |
Timepicker.Content | Portal-rendered dropdown containing time columns |
Timepicker.TimeGrid | Pre-configured layout of Hour, Minute, and Period columns |
Timepicker.HourColumn | Individual hour selection column |
Timepicker.MinuteColumn | Individual minute selection column |
Timepicker.PeriodColumn | AM/PM toggle (only shown in 12h format) |
Timepicker.Footer | Action 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
| Prop | Type | Default | Description |
|---|---|---|---|
value | TimeValue | TimeRange | null | — | Controlled value |
defaultValue | TimeValue | TimeRange | null | — | Default uncontrolled value |
onValueChange | (value) => void | — | Callback when value changes |
format | '12h' | '24h' | '12h' | Time display format |
mode | 'single' | 'range' | 'single' | Single time or time range selection |
label | string | — | Accessible label text |
error | string | — | Error message to display |
disabled | boolean | false | Disable the timepicker |
placeholder | string | 'Select time' | Placeholder text |
minuteStep | number | 5 | Minute increment step |
clearable | boolean | true | Show clear button |
showApplyButton | boolean | false | Show Apply button in footer |
minTime | string | — | Minimum selectable time (HH:MM) |
maxTime | string | — | Maximum selectable time (HH:MM) |
disabledTimes | string[] | — | Array of disabled times (HH:MM) |
disabledHours | number[] | — | 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 columnsArrow Up/Down- Navigate within time columnsEnter/Space- Select focused time itemEscape- Close dropdown without applying
-
Screen Reader Support
- Proper ARIA roles (
listbox,option,combobox) aria-selectedandaria-disabledstates- Descriptive labels for all interactive elements
- Proper ARIA roles (
-
Focus Management
- Visible focus indicators on all focusable elements
- Focus returns to trigger when dropdown closes
- Focus ring uses semantic
--color-focustoken
Styling
The Timepicker uses semantic design tokens from the theme:
| Token | Usage |
|---|---|
bg-surface | Trigger background |
bg-elevated | Dropdown content background |
bg-brand | Selected item background |
border-field | Default border color |
border-focus | Focus border color |
rounded-control | Trigger border radius |
rounded-overlay | Dropdown border radius |
shadow-elevated | Dropdown shadow |