TimeField
A typed time input — format-driven, with Arrow-key stepping and a native Date value.
@garn/ui/time-fieldDefault
A default 24-hour time field.
Twelve Hour
A 12-hour format (h:mm a).
Step
Arrow keys step by 15 minutes.
States
Disabled and invalid.
Installation
garn is copy-in — the garn CLI writes this component's source into your project, so you own and can edit it. It resolves any base files and installs the npm dependencies below for you.
npx garn-ui add time-field- Registry
time-field- Source
- time-field.tsx
- Deps
date-fnslucide-react- Registry deps
utilsdate-iouse-controllable-stateinput
Import
import { TimeField } from "@garn/ui/time-field";Props
Generated from the component's TypeScript types.
appearance- Description
- No description.
- Type
"ghost" | "outline" | "soft"- Default
outline
size- Description
- No description.
- Type
"lg" | "md" | "sm" | "xl" | "xs"- Default
md
defaultValue- Description
- No description.
- Type
Date
format- Description
- date-fns token string driving display, parse, and the placeholder.
- Type
string
hourStep- Description
- Minutes added/removed per PageUp/PageDown. Default 60.
- Type
number
leading- Description
- Override the default leading clock glyph.
- Type
React.ReactNode
onValueChange- Description
- No description.
- Type
(value: Date | undefined) => void
step- Description
- Minutes added/removed per ArrowUp/ArrowDown. Default 1.
- Type
number
value- Description
- Selected time as a `Date` (time portion meaningful; date portion preserved).
- Type
Date
Plus 316 inherited native <input> attributes.
Styling
Target these data-slots and remap these tokens to restyle without forking the component.
[data-slot="input"][data-slot="input-root"][data-slot="input-leading"][data-slot="input-clear"]| Property | Token | Tier |
|---|---|---|
height | --garn-control-h-md | semantic · air |
paddingInline | --garn-control-px-md | semantic · air |
Overview
TimeField is the typed time control — the time-side twin of DatePicker's field. It reuses Input wholesale (the clock leading glyph, clear button, appearance × size, the wrapped focus surface, and the invalid treatment), adding only the time logic: a format prop (date-fns tokens like HH:mm or h:mm a) that drives display, parse, and the placeholder, and Arrow-key stepping (↑/↓ by step minutes, PageUp/Down by the hour). The committed value is a native Date — the time portion is what matters, the date portion is preserved — so a TimeField drops straight into DateTimePicker.
Reach for TimePicker instead when users should pick from a list of slots (booking), and DateTimePicker when a date and time are entered together.
Guidelines
Pair with a Label and pick a format that matches expectations (24-hour HH:mm vs 12-hour h:mm a); it drives typing and display together. Let the field surface own sizing — choose a size rung rather than hand-padding. Reflect validity with aria-invalid + a described message. Arrow keys step the value, so keyboard users never need the mouse.
Wire format: HH:mm:ss. With name, a hidden input posts the canonical time of day (the <input type="time" step="1"> wire format) — never the formatted display text ("9:30 AM") and never a UTC-shifted instant. The visible field carries no name, so nothing double-submits.
Best practices
- Pair with a label and choose a 24h/12h
format. - Hold the value in state (or use
defaultValue); read it as aDate. - Set
stepto the granularity you want Arrow keys to nudge.
- Don't rely on the placeholder as the label.
- Don't hand-set height/padding — the size variant owns the grid.
Content guidelines
- Label with what the time is for ("Start time").
- Use a
formatwhose pattern reads as an example ("HH:mm").
Accessibility
- Role
textbox- Focus
- Inherits Input's focus surface (border darkens on focus; keyboard focus adds a neutral ring).
Enforceable contracts
Give every time field an accessible label.
when no associated <label htmlFor>, aria-label, or aria-labelledby
Keyboard
| ArrowUpArrowDown | Step the time by `step` minutes (default 1). |
| PageUpPageDown | Step the time by `hourStep` minutes (default 60). |
- • Give the field an accessible label — <label htmlFor>, aria-label, or aria-labelledby; the format-pattern placeholder is not a label.
- • The expected format is announced via a visually-hidden aria-describedby hint.
- • Reflect validation with aria-invalid + a described error message.
- Status
- Stable
- Version
v0.3.0- Introduced
v0.2.0
garn doesn't yet record a per-release version history. This reflects the component's current lifecycle state; full changelog entries will appear once the metadata pipeline emits a lifecycle.history field.