Time Field
Edit an hour and minute with segments or a selector.
Sent every weekday at this time.
Installation
pnpm dlx shadcn@4.18.0 add @siteplane/time-fieldComplete the one-time setup first. Registry JSON lists source files and dependencies.
For this page’s examples, also install:
pnpm dlx shadcn@4.18.0 add @siteplane/button @siteplane/fieldset @siteplane/form @siteplane/inputField is installed with Input.
Usage
import { TimeField } from "@/siteplane/components/time-field";
<TimeField aria-label="Meeting time" defaultValue={{ hour: 9, minute: 0 }} />;Controlled value
Pass value and onValueChange to keep the time in React state. onValueChange receives undefined when the field is cleared; store null for an empty controlled value, because undefined makes the field uncontrolled.
Minute step
minuteStep sets the minute options in the time selector, here every 15 minutes. Typed minutes are not rounded, so check the step on the server if it matters.
12-hour clock
Set hourCycle={12} for an application that shows times such as 9:00 PM. The field adds an AM/PM segment after the minutes, which takes focus and arrow keys like the hour and the minute; typing A or P sets it. The selector names each hour with its day period, such as 09 PM. The hour keeps its leading zero, as in every other segment. The value and the submitted HH:mm stay in 24-hour time. The space before AM or PM is a plain space on the server and in the browser alike, so a prerendered page hydrates without a mismatch.
import { TimeField } from "@/siteplane/components/time-field";
<TimeField
aria-label="Quiet hours start"
defaultValue={{ hour: 21, minute: 0 }}
hourCycle={12}
/>;Disabled, read-only and invalid
A disabled Field also disables the time field, and readOnly shows a time that can't be changed on the regular field surface. For an error from your application, set invalid on Field and explain the problem in a FieldError.
Set by your workspace admin.
Submission and reset
With name, TimeField submits a hidden 24-hour HH:mm string without date, seconds or time-zone offset; the example submits { "reminderTime": "09:30" }. Without value or defaultValue the field starts empty and submits an empty string. With required, submitting an empty field is blocked, the field is focused and FieldError shows the required message. Native form reset restores defaultValue; reset controlled state yourself.
Decorative icon
Use iconMode="decorative-start" for compact typed entry, such as a start and end time. The clock is decorative and no selector opens; iconMode="none" removes the icon.
API reference
| Prop | Type | Default | Description |
|---|---|---|---|
aria-label | string | unset | Accessible name when no FieldLabel or aria-labelledby names the field. |
aria-labelledby / aria-describedby | string | unset | External label and help ids; inside a Field, FieldLabel, FieldDescription and FieldError are connected automatically. |
aria-invalid | AriaAttributes[aria-invalid] | unset | External invalid state. |
value / default | TimeFieldValue | null (value only) | uncontrolled / unset | Controlled or initial time; unset or null shows empty segments. |
on | (value: TimeFieldValue | undefined) => void | unset | Reports a completed time, or undefined when cleared. |
disabled | boolean | false | Prevents editing/opening; a disabled Field also disables the field. |
class | string | segment width | Outer control styling and width. By default the field is as wide as its segments and the selector button; a width utility such as w-full or w-48 replaces that. |
read | boolean | false | Read-only field on the regular surface; required blocks form submission while empty, focuses the field and shows the required message in FieldError. |
size | sm | md | lg | xl | provider / lg | Control size. |
text | sm | base | unset | Field text size; unset is 14px, or 16px at xl, like Input. |
variant | default | elevated | inset | soft | default | Input surface. |
form | string | unset | Associated form ID. |
name | string | unset | Hidden serialized input name; a Field name takes precedence. |
icon | action-end | decorative-start | none | action-end | Selector button, decorative leading icon or no icon. |
open | string | Open time selector | Selector button name. |
incomplete | string | Enter a complete time. | The error of a required field while its time is incomplete, shown by FieldError. |
minute | number | 5 | Minute selector options; normalized to an integer between 1 and 60. |
hour | 12 | 24 | 24 | Clock of the segments and the selector. 12 adds the locale's AM/PM segment, named after aria-label like the hour and minute; the value stays in 24-hour time. |
align | start | center | start | Segment text layout; center shows once the field is wider than its segments, for example with w-full. |
hour | string | aria-label + hour / minute | Segment names. A FieldLabel or aria-labelledby follows them; without a value here, the localized hour and minute names are used. |
hours | string | Hours / Minutes | Selector column labels. |
size and variant use the same scale and surfaces as Input; see Input sizes and variants.
Exported types: TimeFieldProps, TimeFieldValue, TimeFieldHourCycle. These describe the contracts above.
Usage notes
TimeFieldValue is {hour:number, minute:number} in 24-hour time, also on a 12-hour clock. It has no date, seconds, locale or time-zone prop. Without value or defaultValue the field shows empty segments. The field is compact by default, as wide as a time takes; add className="w-full" to fill a form column or a grid cell. Inside a Field, FieldLabel names TimeField and clicking it focuses the hour segment; outside a Field, pass aria-label or aria-labelledby. Arrow keys change the focused hour, minute or AM/PM segment. Only the props listed above are supported, and TimeField renders its own segments, selector button and popover; it accepts no children or ref.
The segments read in the direction of the locale that SiteplaneLocaleProvider sets, which the arrow keys between them follow too, and are isolated from the text around them. An en-US time keeps its hour before its minute in a right-to-left page, while the field keeps the page's direction: its selector button sits at the page's inline end.