Time Input
Segmented hours and minutes you can type, arrow or scrub.
Quiet hours
Notifications pause and calls go straight to voicemail.
8 h 30 m of quiet every night
Installation
npx shadcn@latest add https://neonkit.heyaadi.com/r/time-input.jsonUsage
import { TimeInput } from "@/components/ui/time-input"
const [time, setTime] = React.useState("09:30")
<TimeInput label="Alarm" value={time} onValueChange={setTime} />Examples
12 and 24 hours
The clock follows the locale by default. hourCycle forces one. The value is 24-hour either way.
Steps and seconds
minuteStep sets how far the arrow keys move minutes. showSeconds adds a seconds segment.
In a form
With a name, the value submits through a hidden input. Enter in a segment submits the form.
API reference
Also accepts native div attributes, and the ref points at the field (role="group"). className and style go to the field box.
| Prop | Type | Default |
|---|---|---|
value | stringControlled value, "HH:mm" or "HH:mm:ss" in 24-hour time. "" for no time. | None |
defaultValue | stringInitial value when uncontrolled. | None |
onValueChange | (value: string) => voidCalled with the 24-hour value once every segment is filled, and with "" when a filled value is cleared. | None |
hourCycle | 1212 adds an AM/PM segment. Defaults to the locale's clock. | from locale |
locale | stringBCP 47 tag for the default clock, the AM/PM labels and their order. | browser |
showSeconds | booleanAdds a seconds segment and includes seconds in the value. | false |
minuteStep | numberHow far the arrow keys and scrubbing move minutes. Typed minutes can be anything. | 1 |
name | stringSubmits the value with a surrounding form. A form reset puts back the starting time. | None |
required | booleanStops the submit until every segment is filled. A half-typed time gets its own message. | false |
leading | ReactNodeShown before the time. Pass null to remove it. | clock icon |
label | ReactNodeVisible label. Names the group, and clicking it focuses the first segment. | None |
description | ReactNodeHelper text under the field. | None |
error | ReactNodeRed invalid state and message. Replaces description. Pass true for the state without a message. | None |
disabled | booleanBlocks editing and leaves the value out of form data. | false |
color | NeonColorPalette name or hex. Falls back to --neon-accent, then cyan. | inherited |
size | "sm"Field height: 32, 40 or 48px, matching Input. | "md" |
containerClassName | stringClasses for the outer block with the label and message. | None |
Accessibility
- When the browser rejects the field on submit (required, type, pattern), it turns red until it's edited or the form resets. The browser shows its own message.
- The field is a group named by its label, and each part is a spinbutton with aria-valuenow, aria-valuemin, aria-valuemax and a spoken aria-valuetext ("Empty" when blank).
- Every segment is a Tab stop. Arrow Left and Right also move between them.
- Arrow Up and Down step with wrap-around, Home and End jump to the ends, Backspace clears a segment and then steps back, and Delete clears in place.
- Digits fill a segment and move on as soon as no second digit could follow. A separator like : moves on early, and A or P sets the period.
- Pasting a time such as 2:30 pm or 14:30 fills every segment at once.
- Segments are contenteditable with a numeric inputmode, so phones open the number pad.
- Without a visible label, pass aria-label to name the group.