TimePicker
- Component overview: Time selection component for picking hours, minutes, and optional seconds. API follows antd
TimePickerconventions (value/defaultValue/onChange/hourStep/minuteStep/disabledHours/showSecond). - Size baseline: Trigger 32px tall, panel column 106px wide, column max-height 332px, item height 38px, border radius 5px.
- Implementation note: Trigger reuses the DatePicker visual rules. Panel is a multi-column scrollable list rendered inside an
unstyledVisualPopover. Selecting an item commits the change immediately and keeps the panel open so users can adjust other columns. - TimePicker panel Figma spec
Basic Usage
Click the trigger to open the panel. Clicking on any column commits a partial change immediately (you can keep adjusting other columns).
Result
Loading...
Live Editor
const Demo = () => { const [time, setTime] = useState(undefined) return ( <div style={{ padding: '40px 0' }}> <TimePicker className="w-62" value={time} onChange={setTime} placeholder="Select time" /> </div> ) } render(<Demo />)
States
Disabled
Result
Loading...
Live Editor
const Demo = () => { return ( <div style={{ display: 'flex', gap: 16, padding: '40px 0' }}> <TimePicker className="w-62" disabled placeholder="Disabled empty" /> <TimePicker className="w-62" disabled value={new Date(2026, 3, 20, 10, 30)} /> </div> ) } render(<Demo />)
With Value (Clearable)
Hover the trigger to reveal the clear button.
Result
Loading...
Live Editor
const Demo = () => { const [time, setTime] = useState(new Date(2026, 3, 20, 13, 30)) return ( <div style={{ padding: '40px 0' }}> <TimePicker className="w-62" value={time} onChange={setTime} /> </div> ) } render(<Demo />)
Steps
minuteStep={5} matches the Figma reference: minutes are listed as 00, 05, 10 … 55.
Result
Loading...
Live Editor
const Demo = () => { const [time, setTime] = useState(new Date(2026, 3, 20, 13, 0)) return ( <div style={{ padding: '40px 0' }}> <TimePicker className="w-62" value={time} onChange={setTime} minuteStep={5} /> </div> ) } render(<Demo />)
With Seconds
Set showSecond to render the third column.
Result
Loading...
Live Editor
const Demo = () => { const [time, setTime] = useState(new Date(2026, 3, 20, 13, 30, 15)) return ( <div style={{ padding: '40px 0' }}> <TimePicker className="w-72" value={time} onChange={setTime} showSecond /> </div> ) } render(<Demo />)
Disabled Hours
Disable a range of hours via disabledHours. The corresponding items become non-interactive in the panel.
Result
Loading...
Live Editor
const Demo = () => { const [time, setTime] = useState(undefined) return ( <div style={{ padding: '40px 0' }}> <TimePicker className="w-62" value={time} onChange={setTime} disabledHours={() => [0, 1, 2, 3, 4, 5, 6, 22, 23]} placeholder="Working hours only" /> </div> ) } render(<Demo />)
Custom Format
Result
Loading...
Live Editor
const Demo = () => { const [time, setTime] = useState(new Date(2026, 3, 20, 13, 5)) const format = (d) => { const hh = d.getHours() const mm = String(d.getMinutes()).padStart(2, '0') const suffix = hh >= 12 ? 'PM' : 'AM' const h12 = ((hh + 11) % 12) + 1 return `${h12}:${mm} ${suffix}` } return ( <div style={{ padding: '40px 0' }}> <TimePicker className="w-62" value={time} onChange={setTime} formatTime={format} /> </div> ) } render(<Demo />)
Token Table
| Element | Property | Token / Value |
|---|---|---|
| Trigger | Border (default) | Separators/Emphasized#CCCCCC |
| Trigger | Border (active) | Grays/Black#000000 |
| Trigger | Background | Backgrounds/Primary#FFFFFF |
| Trigger (disabled) | Background | Grays/Gray-1#EBEBEB |
| Value text | Color | Labels/Primary#000000 |
| Placeholder | Color | Labels/Tertiary#757575 |
| Item text | Color | Labels/Secondary#3D3D3D |
| Item hover | Background | Grays/Gray-1#EBEBEB |
| Panel | Background | Grays/White#FFFFFF |
| Panel | Shadow | Effects/Shadow/Defaultrgba(0,0,0,0.1) |
| Check icon | Color | Labels/Primary#000000 |
Size Spec
| Dimension | Value |
|---|---|
| Trigger height | 32px |
| Trigger horizontal padding | Spacing_16 / Spacing_12 |
| Trigger border radius | Radius_5 (5px) |
| Trigger font | Body/Regular (14px) |
| Panel column width | 106px |
| Panel column max-height | 332px (scrollable) |
| Item padding | Spacing_8 / Spacing_16 |
| Item height | 38px |
| Check column | 20×20px reserved (no layout shift between selected / unselected) |
| Popover offset | 4px |
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | Date | — | Controlled time value |
defaultValue | Date | — | Uncontrolled initial value |
placeholder | string | 'Select time' | Trigger placeholder text |
formatTime | (date: Date) => string | HH:mm (or HH:mm:ss when showSecond) | Display formatter |
hourStep | number | 1 | Hour column step |
minuteStep | number | 1 | Minute column step |
secondStep | number | 1 | Second column step |
showSecond | boolean | false | Render the second column |
disabledHours | () => number[] | — | Disabled hour values |
disabledMinutes | (hour: number) => number[] | — | Disabled minute values (per selected hour) |
disabledSeconds | (hour: number, minute: number) => number[] | — | Disabled second values |
disabled | boolean | false | Disable the picker |
allowClear | boolean | true | Show clear button on hover |
open | boolean | — | Controlled open state |
defaultOpen | boolean | false | Initial open state |
onOpenChange | (open: boolean) => void | — | Open state change callback |
onChange | (date: Date | undefined) => void | — | Value change callback |
className | string | — | Trigger custom class |
Differences from antd
value/onChangeuse nativeDateinstead ofdayjs.- Selecting any column commits the change immediately; the panel stays open so users can keep tweaking other columns.
use12Hours,showNow, and the range picker variant are not implemented yet.
Changelog
2026-05-29 — Initial release
New component: TimePicker (hours + minutes, optional seconds). Supports hourStep / minuteStep / secondStep, disabledHours / disabledMinutes / disabledSeconds, controlled / uncontrolled value, clearable trigger, controlled open mode, and custom time formatting.