Time Picker
Hour, minute and second columns.
Introduction
Three scrolling columns. Hover fills primary-50 and the current value fills primary-100, with a neutral-100 rule between the columns — measured from the UX4G kit.
Each column is a listbox where selection follows focus, so ↑ and ↓ change the value directly. That is the right pattern for a spinner, and it keeps each column to one tab stop rather than sixty.
For a time behind a trigger, use TimePickerField from Date Picker.
Installation
npx shadcn@latest add https://indiacn.in/r/time-picker.jsonimport { TimeColumns, TimePicker } from '@/components/ui/time-picker';Usage
const [time, setTime] = useState<ITimeValue>({ hours: 9, minutes: 30, seconds: 0 });
return <TimePicker value={time} onValueChange={setTime} showHeader />;Examples
Default
Chosen: 09:30:00
'use client';
import { useState } from 'react';
import { ITimeValue, TimePicker } from '@/components/ui/time-picker';
export default function Component() {
const [time, setTime] = useState<ITimeValue>({ hours: 9, minutes: 30, seconds: 0 });
return <TimePicker value={time} onValueChange={setTime} showHeader />;
}Without seconds
'use client';
import { useState } from 'react';
import { ITimeValue, TimePicker, formatTime } from '@/components/ui/time-picker';
import { Body3 } from '@/components/ui/typography';
export function TimePickerDefault() {
const [time, setTime] = useState<ITimeValue>({ hours: 9, minutes: 30, seconds: 0 });
return (
<div className='flex flex-col gap-4'>
<TimePicker value={time} onValueChange={setTime} showHeader />
<Body3 className='text-neutral-600'>Chosen: {formatTime(time, true)}</Body3>
</div>
);
}
export function TimePickerNoSeconds() {
const [time, setTime] = useState<ITimeValue>({ hours: 14, minutes: 0, seconds: 0 });
return <TimePicker value={time} onValueChange={setTime} showSeconds={false} showHeader />;
}
export function TimePicker12Hour() {
const [time, setTime] = useState<ITimeValue>({ hours: 14, minutes: 30, seconds: 0 });
return (
<div className='flex flex-col gap-4'>
<TimePicker
value={time}
onValueChange={setTime}
hourCycle={12}
showSeconds={false}
showHeader
/>
<Body3 className='text-neutral-600'>
The value stays 24-hour — {time.hours}:{String(time.minutes).padStart(2, '0')} — only the
display changes.
</Body3>
</div>
);
}
Twelve-hour clock
UX4G asks for an AM/PM option and for the format to follow regional preference. hourCycle={12} shows hours as 12, 1, 2 … and adds a fourth column.
The value never changes shape: hours is always 0–23. A component that hands back 7 without saying which seven is worse than useless, so only the display switches.
The value stays 24-hour — 14:30 — only the display changes.
'use client';
import { useState } from 'react';
import { ITimeValue, TimePicker } from '@/components/ui/time-picker';
export default function Component() {
const [time, setTime] = useState<ITimeValue>({ hours: 14, minutes: 30, seconds: 0 });
return (
<TimePicker
value={time}
onValueChange={setTime}
hourCycle={12}
showSeconds={false}
showHeader
/>
);
}Accessibility
| Key | Moves |
|---|---|
| ↑ ↓ | One step, wrapping |
| PageUp PageDown | Ten steps |
| Home End | First and last value |
- Each column is a
listboxlabelled Hour, Minute or Second, and every option carriesaria-selected. - Only the current value is tabbable, so three columns are three tab stops.
- The selected fill is
primary-100withneutraltext, which holds well past 4.5:1 in both themes. - Values are zero-padded and rendered with
tabular-nums, so the columns do not jitter as they scroll.
API Reference
TimePicker
| Prop | Type | Default | Description |
|---|---|---|---|
value | ITimeValue | — | Current time |
onValueChange | (value: ITimeValue) => void | — | Fires on every change |
showSeconds | boolean | true | Renders the third column |
showHeader | boolean | false | Echoes the value above the columns |
onConfirm | () => void | — | Omit it and the Ok button is not rendered |
TimeColumns
The columns with no card, header or footer, for composing into a larger surface. Takes value, onValueChange, showSeconds and hourCycle.
ITimeValue is { hours: number; minutes: number; seconds: number }, with hours always 0–23. The module also exports formatTime(value, showSeconds, hourCycle), toTimeValue(date), toClockHour(hours) and fromClockHour(clockHour, isAfternoon).