ComponentsInputs and forms

Time picker

A time of day, typed or picked from a 15-minute list in the date picker's popover look. 24-hour, in the agency's time zone.

Playground

Change the controls to try each prop. The code under the preview updates as you go and lists only the props you changed; copy it into your page.

States
import { TimePicker } from '@/ui/time-picker';<TimePicker label="Start time" />

Default

Press Down or the clock button for the list, 15 minutes apart, with the current time checked and scrolled into view. Or type: "930", "9.30" and "9:30" all read as 09:30, and the list jumps to the match.

import { Field, FieldLabel } from '@/ui/field';
import { TimePicker } from '@/ui/time-picker';

// Type "930" or "9.30", or press Down for the list. The current time is checked on the selected fill.
export default function StartTime() {
  return (
    <Field className="w-fit">
      <FieldLabel htmlFor="start-time">Start time</FieldLabel>
      <TimePicker id="start-time" label="Start time" defaultValue="14:30" />
    </Field>
  );
}

From now

min starts the list later, such as at the agency's time now when the day is today. step sets the gap between times. A time typed outside the list is kept: the list is a shortcut, not a limit.

import { Field, FieldLabel } from '@/ui/field';
import { TimePicker } from '@/ui/time-picker';

// On today the list starts from now; a time typed earlier than that is still kept.
export default function FromNow() {
  return (
    <Field className="w-fit">
      <FieldLabel htmlFor="call-time">Call at</FieldLabel>
      <TimePicker id="call-time" label="Call at" min="16:20" step={30} />
    </Field>
  );
}

States

Empty shows the format as its placeholder. Off-step times such as 09:10 stay as typed. Invalid takes the danger edge; text that doesn't read as a time says how to fix it when you leave the field and keeps the last good time. Disabled fades while the meeting saves.

import { Field, FieldLabel } from '@/ui/field';
import { TimePicker } from '@/ui/time-picker';

// Leave "25:00" to see the error: it says how to fix it and keeps the last good time.
export default function States() {
  return (
    <div className="flex flex-wrap items-start gap-200">
      <Field className="w-fit">
        <FieldLabel htmlFor="time-empty">Empty</FieldLabel>
        <TimePicker id="time-empty" label="Reminder time" />
      </Field>
      <Field className="w-fit">
        <FieldLabel htmlFor="time-filled">Filled</FieldLabel>
        <TimePicker id="time-filled" label="End time" defaultValue="09:10" />
      </Field>
      <Field className="w-fit">
        <FieldLabel htmlFor="time-invalid">Invalid</FieldLabel>
        <TimePicker id="time-invalid" label="Interview time" defaultValue="13:00" invalid />
      </Field>
      <Field className="w-fit" disabled>
        <FieldLabel htmlFor="time-disabled">Disabled</FieldLabel>
        <TimePicker id="time-disabled" label="Meeting time" defaultValue="14:30" disabled />
      </Field>
    </div>
  );
}

On this page