OPERATOR

TimeField

Usage

Briefing time
0930

Committed wall time: 09:30

const [time, setTime] = useState<string | null>("09:30");
const [timeValid, setTimeValid] = useState(true);
 
<TimeField
  id="briefing-time"
  label="Briefing time"
  value={time}
  onValueChange={setTime}
  onDraftValidityChange={setTimeValid}
  min="08:00"
  max="18:00"
  required
/>
<Button disabled={!timeValid}>Review</Button>

Wall-time values

The controlled value is an exact 24-hour HH:mm string from 00:00 through 23:59, or null. It carries no date or time zone. min and max are inclusive times in the same format. Bounds must be in ascending order; represent an overnight schedule with separate dates or instants.

Connect onDraftValidityChange to the form's Review and Save controls. It reports initial and changed validity. A partial, invalid, out-of-bounds, or required-empty edit reports false even when the public value still contains an earlier valid time. Optional, fully empty fields are valid. Moving focus does not clear a rejected edit.

To discard an unfinished draft while retaining the same controlled value, change the component's React key. An unchanged value alone cannot signal that the host intends to discard local editing.

Keyboard and focus

React Aria labels the hour and minute segments and provides arrow-key editing and movement between segments. The id and forwarded HTMLDivElement ref identify the time-control group. Focusing the group enters the first segment, which lets FormErrorSummary target it. disabled and readOnly prevent changes.

The callback receives (valid, { pending }). Use valid to gate Review/Save and pending to track unfinished local editing for leave protection or draft persistence. A pristine required-empty field is invalid but has pending: false; partial input has pending: true. Malformed supplied values also remain clean until edited. Existing callbacks that accept only valid continue to work.

Props

PROPTYPEDEFAULTREQUIRED
labelstring—yes
value

HH:mm from 00:00 through 23:59, or null.

string—yes
onValueChange

Requests a complete valid time or null after clearing.

function—yes
onDraftValidityChange

Callback (valid, { pending }). Pending identifies uncommitted local edits; pristine required-empty or malformed supplied values are invalid but not pending. Reports initial and changed validity. False for partial, out-of-bounds, or required-empty drafts even while value still contains an earlier valid time. Block Review/Save while false.

function—no
min

Inclusive HH:mm lower bound.

string—no
max

Inclusive HH:mm upper bound. Overnight bounds are not supported.

string—no
disabledbooleanfalseno
readOnlybooleanfalseno
requiredbooleanfalseno
id

ID of the focusable time-control group. Focusing it or its forwarded HTMLDivElement ref enters the first segment.

string—no
classNamestring—no

Accessibility

  • Invalid drafts expose invalid state and a readable error. An edit outside min or max names the bound it crossed: "Choose a time at or before 18:00."
  • Segments use tabular numerals, text of at least 11px, 24 CSS pixel minimum targets, and the shared focus indicator.
  • Time validity remains separate from the last complete value, so unfinished edits can block submission.

Manifest entry

<TimeField id="start-time" label="Start time" value={time} onValueChange={setTime} onDraftValidityChange={setTimeValid} required />
View agent manifest
{
  "description": "Controlled 24-hour wall-time field with segmented editing and explicit draft validity. It carries no date or time zone.",
  "props": {
    "label": {
      "type": "string",
      "required": true
    },
    "value": {
      "type": "string",
      "required": true,
      "description": "HH:mm from 00:00 through 23:59, or null."
    },
    "onValueChange": {
      "type": "function",
      "required": true,
      "description": "Requests a complete valid time or null after clearing."
    },
    "onDraftValidityChange": {
      "type": "function",
      "description": "Callback (valid, { pending }). Pending identifies uncommitted local edits; pristine required-empty or malformed supplied values are invalid but not pending. Reports initial and changed validity. False for partial, out-of-bounds, or required-empty drafts even while value still contains an earlier valid time. Block Review/Save while false."
    },
    "min": {
      "type": "string",
      "description": "Inclusive HH:mm lower bound."
    },
    "max": {
      "type": "string",
      "description": "Inclusive HH:mm upper bound. Overnight bounds are not supported."
    },
    "disabled": {
      "type": "boolean",
      "default": false
    },
    "readOnly": {
      "type": "boolean",
      "default": false
    },
    "required": {
      "type": "boolean",
      "default": false
    },
    "id": {
      "type": "string",
      "description": "ID of the focusable time-control group. Focusing it or its forwarded HTMLDivElement ref enters the first segment."
    },
    "className": {
      "type": "string"
    }
  },
  "composable_with": [
    "FormSection",
    "FormErrorSummary",
    "DateTimePicker"
  ],
  "never_inside": [
    "Button"
  ],
  "example": "<TimeField id=\"start-time\" label=\"Start time\" value={time} onValueChange={setTime} onDraftValidityChange={setTimeValid} required />"
}

On this page