Time Input
HOUR/MINUTE (+ SECOND) + AM/PM time entry composed from NumberInput + SegmentGroup — Ark ships no time-picker machine, so this is the kit's own composed time field.
TimeInput is a standalone time entry — HOUR / MINUTE (+ SECOND) steppers next to an AM/PM toggle, composed from NumberInput and SegmentGroup. Ark UI has no time-picker machine (only a countdown timer), so this is hand-composed from the kit's own primitives rather than a wrapped Ark part — it's the same time row DatePicker will embed in its popover once it lands.
import { TimeInput } from '@42-central/ui-react/time-input';
<TimeInput label="Meeting time" defaultValue={{ hour: 9, minute: 30 }} />The value is { hour, minute, second? } | null — hour is always stored in 24h, regardless of what's displayed. There's no built-in range mode: a "start" / "end" pair reads better as two labelled TimeInputs than as one control split down the middle with nothing to tell its two halves apart — see Start / end pairs.
Playground
Component
Dir
Presets
Props
Code
<Component
label="Meeting time"
minuteStep={1}
placeholder="--"
/>12h vs 24h
Display defaults to the active locale's own convention (Intl.DateTimeFormat's hourCycle) — the AM/PM segment group only renders in a 12h locale. Override it with format (12 | 24) or hourCycle for the exact Unicode cycle ("h11" | "h12" | "h23" | "h24"). The stored hour is always 24h either way — only the display and the AM/PM segment change.
// Force 24h regardless of locale
<TimeInput format={24} defaultValue={{ hour: 14, minute: 5 }} />
// Force 12h regardless of locale
<TimeInput format={12} defaultValue={{ hour: 14, minute: 5 }} />Start / end pairs
There's no range prop. Two TimeInputs, each with its own label, reads more clearly than one control with an unlabelled start half and end half — compose them yourself:
Code
<div className="flex gap-4">
<TimeInput label="Start time" defaultValue={{ hour: 9, minute: 0 }} />
<TimeInput label="End time" defaultValue={{ hour: 17, minute: 30 }} />
</div>Seconds & minute step
withSeconds adds a third stepper; minuteStep sets the minute increment (e.g. 15 for quarter-hour scheduling). Every stepper wraps at its bounds (23 → 0, 59 → 0, …) instead of clamping. Typing doesn't wrap: each field behaves like a clock segment, so a digit that would overflow it replaces the field (10, then 2 typed at the start, reads 2 — not 210), and an out-of-range value is clamped when the field loses focus.
Sizes
Five sizes — xs through xl — matching the rest of the family.
Code
<TimeInput size="xs" defaultValue={{ hour: 9, minute: 30 }} />
<TimeInput size="sm" defaultValue={{ hour: 9, minute: 30 }} />
<TimeInput size="md" defaultValue={{ hour: 9, minute: 30 }} />
<TimeInput size="lg" defaultValue={{ hour: 9, minute: 30 }} />
<TimeInput size="xl" defaultValue={{ hour: 9, minute: 30 }} />Label, description & error
label / description / error / required come from the built-in Field wrapper, same as the rest of the family.
Code
<TimeInput
label="Meeting time"
description="Local time for all attendees"
defaultValue={{ hour: 9, minute: 30 }}
required
/>
<TimeInput
label="Meeting time"
error="Pick a time"
defaultValue={{ hour: 0, minute: 0 }}
/>Controlled
Drive the value from state. onChange hands you the next { hour, minute, second? }.
'use client';
import { useState } from 'react';
import { TimeInput, type TimeValue } from '@42-central/ui-react/time-input';
export function MeetingTime() {
const [value, setValue] = useState<TimeValue>({ hour: 9, minute: 30 });
return <TimeInput label="Meeting time" value={value} onChange={setValue} />;
}Forms
TimeInput has no machine-backed hidden part to piggyback on (unlike Select's hidden <select>), so name renders a hand-rolled hidden HH:mm[:ss] input instead — a <form> submission and FormData pick it up the same way.
<form action={save}>
<TimeInput name="startTime" defaultValue={{ hour: 9, minute: 0 }} />
</form>API
Prop
Type
TimeInput has no single focusable control — Hour/Minute/Second/Period are separate sub-fields —
so testId lands on the wrapping shell <div> rather than any one of them. See the
Testing guide for the full testId convention across the kit.
Accessibility
- HOUR / MINUTE / SECOND are real
role="spinbutton"NumberInputs, each with its own accessible name (Hour/Minute/Second) and the full stepper keyboard model (↑/↓,PageUp/PageDown,Home/End). The name stays wired even though the label isn't shown — it's visually hidden (sr-only), not removed, so screen readers still announce it. Theh/m/ssuffix next to each value is decorative only (aria-hidden), not a replacement accessible name. - The AM/PM toggle is a labeled
SegmentGroup(a radio-group under the hood) — arrow keys move between the two segments. Its "Period" label is likewise visually hidden but still the accessible name; the segments' own "AM"/"PM" text is visible and self-descriptive. - Every stepper wraps at its bounds rather than clamping, so keyboard users can cycle past midnight/the hour without hitting a dead end.
- The row is one
role="group"named by the field'slabel(througharia-labelledby) and described by itsdescription/error, so a screen reader entering it hears "Meeting time, group" before "Hour". TwoTimeInputs labelled "Start time" and "End time" are two distinctly named groups. Without alabelthe group falls back to "Time" (translations.time), so it's never anonymous.←/→at the edge of a field's text move to the neighbouring field (and from the last one onto the AM/PM toggle), so the caret crosses the row as if it were one input. Mid-text, the arrows move the caret as usual; right-to-left locales mirror the keys. invalid/errorpropagate to every sub-control'saria-invalid;label/description/errorassociate through the built-inField.
Changelog
- #250
DatePicker's
typeableworks withwithTime, and TimeInput moves between fields with the arrow keys
- #45
MultiSelect's
searchPlaceholder, DatePicker'stimePlaceholderand TimeInput'splaceholder
- #16
New
TimeInput: hour, minute and optional second fields, withformat12 or 24
Calendar
Inline month grid on Ark UI's date-picker machine — single or range selection, restriction predicates, event dots, and month/year navigation.
DatePicker
A trigger + popover calendar on Ark UI's date-picker machine, composing Calendar's grid and TimeInput's time row — range and withTime cover all four date combinations.