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/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.
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/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
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.
invalid/errorpropagate to every sub-control'saria-invalid;label/description/errorassociate through the built-inField.
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.