Time Field

PreviousNext

A time field allows users to enter and edit time values using a keyboard.

Event time
––––AM
import { TimeField } from "@/components/ui/time-field"

export function TimeFieldDemo() {
  return <TimeField label="Event time" />
}

Installation

pnpm dlx shadcn@latest add time-field

Usage

import { TimeField } from "@/components/ui/time-field"
<TimeField
  label="Event time"
  description="Select a time for your event."
/>

About

The TimeField component is built on top of React Aria Components and uses the @internationalized/date library for time manipulation and formatting.

Examples

Default

Event time
––––AM
import { TimeField } from "@/components/ui/time-field"

export function TimeFieldDemo() {
  return <TimeField label="Event time" />
}
<TimeField label="Event time" />

With Description

Meeting time
––––AM
Enter the time for your meeting.
import { TimeField } from "@/components/ui/time-field"

export function TimeFieldDescriptionDemo() {
  return (
    <TimeField
      label="Meeting time"
      description="Enter the time for your meeting."
    />
  )
}
<TimeField
  label="Meeting time"
  description="Enter the time for your meeting."
/>

Disabled

Event time
––––AM
import { TimeField } from "@/components/ui/time-field"

export function TimeFieldDisabledDemo() {
  return <TimeField label="Event time" isDisabled />
}
<TimeField label="Event time" isDisabled />

Granularity

Hour and minute
––––AM
Hour, minute, and second
––––––AM
import { TimeField } from "@/components/ui/time-field"

export function TimeFieldGranularityDemo() {
  return (
    <div className="flex flex-col gap-4">
      <TimeField label="Hour and minute" granularity="minute" />
      <TimeField label="Hour, minute, and second" granularity="second" />
    </div>
  )
}
<div className="flex flex-col gap-4">
  <TimeField label="Hour and minute" granularity="minute" />
  <TimeField label="Hour, minute, and second" granularity="second" />
</div>

The granularity prop determines which time segments are displayed:

  • hour - Only hour segment
  • minute - Hour and minute segments (default)
  • second - Hour, minute, and second segments

Hour Cycle

12-hour format
––––AM
24-hour format
––––
import { TimeField } from "@/components/ui/time-field"

export function TimeFieldHourCycleDemo() {
  return (
    <div className="flex flex-col gap-4">
      <TimeField label="12-hour format" hourCycle={12} />
      <TimeField label="24-hour format" hourCycle={24} />
    </div>
  )
}
<div className="flex flex-col gap-4">
  <TimeField label="12-hour format" hourCycle={12} />
  <TimeField label="24-hour format" hourCycle={24} />
</div>

The hourCycle prop controls whether the time field uses 12-hour or 24-hour format:

  • 12 - 12-hour format with AM/PM
  • 24 - 24-hour format

Validation

Meeting time
––––AM
Select a time between 9 AM and 5 PM.
"use client"

import { useState } from "react"
import { Time } from "@internationalized/date"

import { Button } from "@/components/ui/button"
import { TimeField } from "@/components/ui/time-field"

export function TimeFieldValidationDemo() {
  const [time, setTime] = useState<Time | null>(null)

  return (
    <form
      className="flex flex-col gap-4"
      onSubmit={(e) => {
        e.preventDefault()
        alert(`Time submitted: ${time?.toString()}`)
      }}
    >
      <TimeField
        label="Meeting time"
        description="Select a time between 9 AM and 5 PM."
        isRequired
        value={time}
        onChange={setTime}
        minValue={new Time(9)}
        maxValue={new Time(17)}
        errorMessage="Please select a time between 9 AM and 5 PM."
      />
      <Button type="submit" className="w-fit">
        Submit
      </Button>
    </form>
  )
}
"use client"
 
import { useState } from "react"
import { Time } from "@internationalized/date"
 
import { Button } from "@/components/ui/button"
import { TimeField } from "@/components/ui/time-field"
 
export default function TimeFieldValidation() {
  const [time, setTime] = useState<Time | null>(null)
 
  return (
    <form
      className="flex flex-col gap-4"
      onSubmit={(e) => {
        e.preventDefault()
        alert(`Time submitted: ${time?.toString()}`)
      }}
    >
      <TimeField
        label="Meeting time"
        description="Select a time between 9 AM and 5 PM."
        isRequired
        value={time}
        onChange={setTime}
        minValue={new Time(9)}
        maxValue={new Time(17)}
        errorMessage="Please select a time between 9 AM and 5 PM."
      />
      <Button type="submit" className="w-fit">
        Submit
      </Button>
    </form>
  )
}

Controlled

Appointment time
900AM

Selected time: 09:00:00

"use client"

import { useState } from "react"
import { Time } from "@internationalized/date"

import { TimeField } from "@/components/ui/time-field"

export function TimeFieldControlledDemo() {
  const [time, setTime] = useState(new Time(9, 0))

  return (
    <div className="flex flex-col gap-4">
      <TimeField label="Appointment time" value={time} onChange={setTime} />
      <p className="text-muted-foreground text-sm">
        Selected time: {time.toString()}
      </p>
    </div>
  )
}
"use client"
 
import { useState } from "react"
import { Time } from "@internationalized/date"
 
import { TimeField } from "@/components/ui/time-field"
 
export default function TimeFieldControlled() {
  const [time, setTime] = useState(new Time(9, 0))
 
  return (
    <div className="flex flex-col gap-4">
      <TimeField label="Appointment time" value={time} onChange={setTime} />
      <p className="text-sm text-muted-foreground">
        Selected time: {time.toString()}
      </p>
    </div>
  )
}

API Reference

TimeField

PropTypeDefaultDescription
labelstring-Label text for the time field
descriptionstring-Help text displayed below the input
errorMessagestring | ((validation: ValidationResult) => string)-Error message for validation
valueTimeValue-The current value (controlled)
defaultValueTimeValue-The default value (uncontrolled)
onChange(value: TimeValue) => void-Handler called when the value changes
minValueTimeValue-The minimum allowed time
maxValueTimeValue-The maximum allowed time
granularity"hour" | "minute" | "second""minute"Determines the smallest unit editable
hourCycle12 | 24-Whether to use 12 or 24 hour time
isDisabledbooleanfalseWhether the time field is disabled
isReadOnlybooleanfalseWhether the time field is read-only
isRequiredbooleanfalseWhether the time field is required
placeholderValueTimeValue-A placeholder time that controls the default values of segments

See the React Aria TimeField documentation for more props and detailed information.

Internationalization

The TimeField component automatically formats times based on the user's locale. The @internationalized/date library handles time formatting and localization.

import { Time } from "@internationalized/date"
 
// Create a time object (24-hour format)
const time = new Time(14, 30) // 2:30 PM
 
// With seconds
const timeWithSeconds = new Time(14, 30, 45) // 2:30:45 PM

Time Zones

The Time type represents a clock time without a date or time zone. For date and time with time zones, use ZonedDateTime:

import { parseZonedDateTime } from "@internationalized/date"
 
const zonedTime = parseZonedDateTime("2024-12-25T14:30[America/New_York]")

See the @internationalized/date documentation for more information on time zones.

Common Use Cases

Business Hours

Restrict time selection to business hours:

import { Time } from "@internationalized/date"
import { TimeField } from "@/components/ui/time-field"
 
<TimeField
  label="Meeting time"
  description="Select a time between 9 AM and 5 PM."
  minValue={new Time(9)}
  maxValue={new Time(17)}
/>

Appointment Scheduling

Show time in 30-minute increments by using a controlled component:

"use client"
 
import { useState } from "react"
import { Time } from "@internationalized/date"
import { TimeField } from "@/components/ui/time-field"
 
export default function AppointmentTime() {
  const [time, setTime] = useState(new Time(9, 0))
 
  const handleChange = (newTime: Time) => {
    // Round to nearest 30 minutes
    const minutes = Math.round(newTime.minute / 30) * 30
    setTime(new Time(newTime.hour, minutes))
  }
 
  return (
    <TimeField
      label="Appointment time"
      value={time}
      onChange={handleChange}
    />
  )
}
  • Date Field - For entering dates
  • Date Picker - Combines a date field with a calendar
  • Field - For composing accessible form fields