Skip to main content

TimePicker

Hour + minute (+ optional seconds) selection. 12- or 24-hour mode.

import { TimePicker } from '@kalyx/react';

Basic usage

import { useState } from 'react';
import { TimePicker, type ISODateString } from '@kalyx/react';

function Example() {
const [time, setTime] = useState<ISODateString | null>(null);
return (
<TimePicker value={time} onChange={setTime} format="24h" step={15}>
<TimePicker.Input />
<TimePicker.HourList />
<TimePicker.MinuteList />
</TimePicker>
);
}

The value is still an ISO 8601 UTC string — the date part acts as a placeholder. Use getTime(iso) from @kalyx/core if you need just the hours/minutes.

Try it live

Live Editor
function Basic24h() {
  const [time, setTime] = React.useState(null);
  return (
    <TimePicker value={time} onChange={setTime} format="24h" step={15}>
      <TimePicker.Input className="kx-live-input" style={{ minWidth: '8rem' }} />
      <div className="kx-live-row" style={{ marginTop: 8 }}>
        <TimePicker.HourList
          classNames={{
            root: 'kx-live-list',
            option: 'kx-live-option',
            optionSelected: 'kx-live-option-selected',
          }}
        />
        <TimePicker.MinuteList
          classNames={{
            root: 'kx-live-list',
            option: 'kx-live-option',
            optionSelected: 'kx-live-option-selected',
          }}
        />
      </div>
      <div className="kx-live-value" style={{ marginTop: 8 }}>
        Selected: <code>{time ?? 'null'}</code>
      </div>
    </TimePicker>
  );
}
Result
Loading...

<TimePicker> (Root)

PropTypeDefaultDescription
valueISODateString | nullControlled time.
defaultValueISODateStringUncontrolled initial value.
onChange(value: ISODateString | null) => voidFires on any hour / minute / period change.
format'12h' | '24h''24h'Time format.
stepnumber1Minute granularity (e.g. 5, 15, 30).
withSecondsbooleanfalseShow seconds in display + input.
displayTimezonestringIANA zone. When set, the hour/minute controls read and write time as observed in this zone (DST-aware). See Timezone.
disabledbooleanfalseDisable the whole picker.
readOnlybooleanfalsePrevent changes.
filterTime(hours: number, minutes: number) => booleanPer-slot disable predicate. Return true to make a slot unselectable (same polarity as MUI X's shouldDisableTime — note this is the inverse of react-datepicker's filterTime, which returns true to keep a slot). An hour is disabled only when every step minute within it returns true. Always receives 24-hour values, regardless of format.
labelsPartial<TimePickerLabels>Override ARIA labels. Keys: timeInput, hourList, minuteList, amPmToggle, hourOption(h), minuteOption(m).
childrenReactNodeSub-components.

<TimePicker.Input>

A text input showing HH:MM (or HH:MM:SS when withSeconds). Parses on Enter / blur.

  • Extends <input> attributes except value, onChange, type.
  • aria-label defaults to "Time input".

<TimePicker.HourList>

A role="listbox" of available hours.

PropTypeDescription
classNamesTimePickerHourListClassNamesStyling.
type TimePickerHourListClassNames = {
root?: string; // <ul>
option?: string; // <li role="option">
optionSelected?: string;
};

Hour set:

  • format="24h"0–23
  • format="12h"1–12 (AM/PM managed by <AmPmToggle>)

<TimePicker.MinuteList>

A role="listbox" of minutes, filtered by step.

PropTypeDescription
classNamesTimePickerMinuteListClassNamesStyling (same shape as HourList).

<TimePicker.AmPmToggle>

A role="radiogroup" with two role="radio" buttons — only renders in format="12h".

PropTypeDescription
classNamesTimePickerAmPmToggleClassNamesStyling.
type TimePickerAmPmToggleClassNames = {
root?: string;
button?: string;
buttonSelected?: string;
};

Patterns

12-hour mode

<TimePicker value={time} onChange={setTime} format="12h" step={15}>
<TimePicker.Input />
<div className="flex gap-2">
<TimePicker.HourList />
<TimePicker.MinuteList />
<TimePicker.AmPmToggle />
</div>
</TimePicker>
Live Editor
function TwelveHour() {
  const [time, setTime] = React.useState(null);
  return (
    <TimePicker value={time} onChange={setTime} format="12h" step={15}>
      <TimePicker.Input className="kx-live-input" style={{ minWidth: '8rem' }} />
      <div className="kx-live-row" style={{ marginTop: 8 }}>
        <TimePicker.HourList
          classNames={{
            root: 'kx-live-list',
            option: 'kx-live-option',
            optionSelected: 'kx-live-option-selected',
          }}
        />
        <TimePicker.MinuteList
          classNames={{
            root: 'kx-live-list',
            option: 'kx-live-option',
            optionSelected: 'kx-live-option-selected',
          }}
        />
        <TimePicker.AmPmToggle
          classNames={{
            root: 'kx-live-ampm',
            button: 'kx-live-ampm-btn',
            buttonSelected: 'kx-live-ampm-selected',
          }}
        />
      </div>
    </TimePicker>
  );
}
Result
Loading...

With seconds

<TimePicker value={time} onChange={setTime} withSeconds>
<TimePicker.Input />
</TimePicker>
Live Editor
function WithSeconds() {
  const [time, setTime] = React.useState(null);
  return (
    <TimePicker value={time} onChange={setTime} withSeconds>
      <TimePicker.Input className="kx-live-input" style={{ minWidth: '8rem' }} />
      <div className="kx-live-value" style={{ marginTop: 8 }}>
        Selected: <code>{time ?? 'null'}</code>
      </div>
    </TimePicker>
  );
}
Result
Loading...

Extracting TimeValue for logic

import { TimePicker } from '@kalyx/react';
import { getTime } from '@kalyx/core';

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

useEffect(() => {
if (!time) return;
const { hours, minutes } = getTime(time); // { hours: 9, minutes: 30, seconds: 0 }
analytics.track('time_set', { hours, minutes });
}, [time]);

return (
<TimePicker value={time} onChange={setTime}>
<TimePicker.Input />
</TimePicker>
);
}