IndiaCN UI

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.json
import { 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

09:30:00

Chosen: 09:30:00

TimePickerDefault
'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

14:00
TimePickerNoSeconds
'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.

02:30 PM

The value stays 24-hour — 14:30 — only the display changes.

TimePicker12Hour
'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

KeyMoves
↑ ↓One step, wrapping
PageUp PageDownTen steps
Home EndFirst and last value
  • Each column is a listbox labelled Hour, Minute or Second, and every option carries aria-selected.
  • Only the current value is tabbable, so three columns are three tab stops.
  • The selected fill is primary-100 with neutral text, 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

PropTypeDefaultDescription
valueITimeValue—Current time
onValueChange(value: ITimeValue) => void—Fires on every change
showSecondsbooleantrueRenders the third column
showHeaderbooleanfalseEchoes 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).

On this page