TimePicker
Two small fields, hours and minutes, that move the caret along as you type. There is no clock, no drop-down and — despite the 12-hour option — no AM/PM control.
Use this when / not when
- Use for a time typed beside a date, or on its own where the date does not matter.
- Not on its own for a 12-hour interface. The component formats hours 1–12 but renders nothing to
choose AM or PM; you supply
meridiemand build that control.DateTimePickeris the one that already has it. - Not for a duration. The value comes back as a point in time, carrying a date.
- Not where the value must be driven from outside:
initialTimeis read once.
Import
import { TimePicker } from "@onlyoffice/apps-ui-kit/components/time-picker";
Also exported from the root barrel @onlyoffice/apps-ui-kit.
Needs ThemeProvider from @onlyoffice/apps-ui-kit/providers/theme.
Times come back as Luxon DateTime objects.
Stories
Default
A 24-hour picker preset to 10:30 (initialTime); type 9 in the hours field and watch it become 09 and jump to minutes; change any other prop live in the Controls panel below.
With Error
The border turns red to flag a time the form rejected (hasError); the fields stay editable so it can be corrected in place.
Twelve Hour Format
Hours stop at 12 (isTwelveHourFormat); nothing on screen tells AM from PM, only the meridiem behind each box decides what onChange receives: the first reports 10:30, the second, showing 02:30, reports 14:30.
Focus On Render
The picker opens with the hours field selected (focusOnRender), so a form that asks for a time first takes the digits without a click; type 14 and the caret moves on to minutes.
Css Customization
The TimePicker and inner TextInput variables set on one wrapper -- the variables are listed under CSS variables on this page. The first box shows the border, background, size and radius variables and the inner fields' text colour; click into it to see --time-input-focus-border. The second adds hasError, the only state in which --time-input-error-border has anything to colour. --text-input-bg is set to the same value as --time-input-bg so the fields blend into the box.
Minimal example
onChange gives a full DateTime: the date from initialTime, the time from the fields.
import { useState } from "react";
import { DateTime } from "luxon";
import { TimePicker } from "@onlyoffice/apps-ui-kit/components/time-picker";
export function ReminderTime({ day }: { day: DateTime }) {
const [at, setAt] = useState(day);
return (
<div>
<TimePicker initialTime={day} onChange={setAt} />
<p>Reminder at {at.toFormat("HH:mm")}</p>
</div>
);
}
Props
Recipes
Error
hasError recolours the group around both fields. It prints no message, and there is no
validation of its own beyond rejecting impossible numbers.
import { useState } from "react";
import { DateTime } from "luxon";
import { TimePicker } from "@onlyoffice/apps-ui-kit/components/time-picker";
import { Text } from "@onlyoffice/apps-ui-kit/components/text";
export function OfficeHours({ day }: { day: DateTime }) {
const [at, setAt] = useState(day.set({ hour: 9, minute: 0 }));
const isOutside = at.hour < 9 || at.hour >= 18;
return (
<div>
<TimePicker initialTime={at} onChange={setAt} hasError={isOutside} />
{isOutside ? (
<Text fontSize="12px">Choose a time between 09:00 and 18:00</Text>
) : null}
</div>
);
}
A 12-hour clock
The component will format hours 1–12, but the meridiem is yours: hold it, render the control, and pass it in so the typed time is parsed into the right half of the day.
import { useState } from "react";
import { DateTime } from "luxon";
import {
ComboBox,
type TOption,
} from "@onlyoffice/apps-ui-kit/components/combobox";
import { TimePicker } from "@onlyoffice/apps-ui-kit/components/time-picker";
const HALVES: TOption[] = [
{ key: "AM", label: "AM" },
{ key: "PM", label: "PM" },
];
export function TwelveHourTime({ day }: { day: DateTime }) {
const [at, setAt] = useState(day);
const [half, setHalf] = useState<TOption>(HALVES[0]);
return (
<div style={{ display: "flex", gap: 8 }}>
<TimePicker
initialTime={day}
onChange={setAt}
isTwelveHourFormat
meridiem={String(half.key)}
/>
<ComboBox
options={HALVES}
selectedOption={half}
onSelect={setHalf}
scaledOptions
/>
<span>{at.toFormat("hh:mm a")}</span>
</div>
);
}
Behaviour the types don't state
- The value carries a date.
onChangehands back aDateTimebuilt frominitialTime's date plus the typed time, so withoutinitialTimeevery value is on today's date at the typed hour. initialTimeis read once. There is no effect syncing it, so the fields cannot be driven from outside after mount; remount with akeyif they must be.- Typing moves the caret for you. Two digits in hours jumps to minutes, and a first digit that cannot start a valid hour — above 2, or above 1 in 12-hour mode — is padded with a zero and jumps as well. The same happens between minutes and blur.
- Out-of-range input is dropped, not corrected. Minutes above 59 call
onBlurand leave the field as it was; letters are ignored entirely. A second hour digit that would pass 23 (12 in 12-hour mode) is dropped: the first digit stays, padded to0x, and the caret moves to minutes. - Clearing a field sets it to
00and reports that time, so neither field can be left empty. - A single digit is zero-padded on blur, so
9becomes09. isTwelveHourFormatrenders no AM/PM control. It changes the hour format and the maximum hour;meridiemis a plain string you pass, and the types require it alongsideisTwelveHourFormat, because without it the typed time cannot be parsed andonChangeis never called.classNameInputis a prefix, not a class. The fields get<classNameInput>-hours-inputand-minutes-input; leave the prop out and they get no extra class.- The error colour survives focus. With
hasErrorthe border keeps the error colour while a field is being edited, instead of switching to the focus colour. - The right-click menu is suppressed on both fields.
- The group is always laid out left to right, hours then minutes, even inside a
right-to-left interface: the wrapper sets
direction: ltr. - The two fields are separated by a literal
:text node, and clicking anywhere in the group that is not the minutes field selects the hours.
CSS variables
Set these on any ancestor; the group reads them through a fallback to the theme token or to a fixed size.
The two fields inside are TextInputs drawn without a border, so its
own variables reach them too: --text-input-color sets the digits' colour, and
--text-input-bg their background, which is painted over the group's and should be set to the
same value as --time-input-bg for the fields to blend in.
Accessibility
- Both fields are real inputs with
aria-labels of "Hours" and "Minutes", andinputMode="numeric"brings up a number keypad. Tab reaches both fields on its own;tabIndexmoves them in the order and reaches both. role="group"andaria-label="Time picker"are hardcoded English on the wrapper and cannot be overridden.- The automatic jump between fields moves focus while the user is typing, which is disorienting with a screen reader and makes correcting a mistake awkward.
hasErroris a colour only — it sets noaria-invalidand prints no message.- Suppressing the context menu removes paste-by-right-click.
Test ids
The two fields carry theirs as data-test-id, with a hyphen, not data-testid; only the group uses the usual attribute.
Related
DateTimePicker— this with a date and a working AM/PM.TextInput— the two fields inside.ComboBox— what to build the meridiem control from.