f-ui
Components

Time Picker

Single-time picker with integrated segmented field, clock icon, and popover time spinner with plain Date values.

Time Picker combines a segmented time field (React Aria hour/minute/AM·PM slots) and a clock icon inside one bordered shell, with column spinners in a popover. Values stay plain Date — only the clock portion is read or written.

Interactions

EventBehavior
Focus a time segmentDoes not open the spinner popover (open via clock trigger).
Click the clock iconToggles the popover open or closed.
Select or type a timeUpdates the value (or the draft when needConfirm).
Press NowSets the current time.
Escape or click outsideCloses the popover.

Design guide

  • Picker over native — Prefer this component (or Time Input for inline typing) over type="time" so styling stays on your design tokens.
  • Format drives columns — Set format (e.g. HH:mm:ss) to control which spinner columns appear; use12Hours switches the default 12h template.
  • Step for booking — Use step={{ minute: 15 }} (or hour/second) to snap spinner options to appointment slots.
  • Business hours — Use disabledTime to grey out or hide hours/minutes/seconds outside allowed windows.
  • Select commits by default — Spinner selection commits immediately; set needConfirm when the footer OK button should apply the draft.

Installing

pnpm dlx shadcn@latest add https://ui.isaacfei.com/r/time-picker.json

Usage

import { useState } from "react";
import { TimePicker } from "@/components/f-ui/time-picker/time-picker";

export function Example() {
  const [time, setTime] = useState<Date | null>(null);
  return (
    <TimePicker
      label="Start time"
      value={time}
      onChange={(next) => setTime(next ?? null)}
    />
  );
}

Embed only the field (no label) via TimePickerFieldControl — used inside calendar footers and tables:

import { TimePickerFieldControl } from "@/components/f-ui/time-picker/time-picker";

Examples

Basic

930AM

Click the clock icon or focus a segment to open the spinner.

"use client";

import { useState } from "react";

import { TimePicker } from "@/components/f-ui/time-picker/time-picker";

export function TimePickerDemo() {
  const [time, setTime] = useState<Date | null>(
    () => new Date(2026, 4, 21, 9, 30, 0, 0),
  );

  return (
    <div className="max-w-xs w-full">
      <TimePicker
        label="Meeting time"
        value={time}
        onChange={(next) => setTime(next ?? null)}
        description="Click the clock icon or focus a segment to open the spinner."
      />
    </div>
  );
}

12-Hour With Seconds

205PM

12-hour clock with second precision.

"use client";

import { useState } from "react";

import { TimePicker } from "@/components/f-ui/time-picker/time-picker";

export function TimePicker12hDemo() {
  const [time, setTime] = useState<Date | null>(
    () => new Date(2026, 4, 21, 14, 5, 30, 0),
  );

  return (
    <div className="max-w-xs w-full">
      <TimePicker
        label="Reminder"
        value={time}
        onChange={(next) => setTime(next ?? null)}
        use12Hours
        format="hh:mm:ss a"
        description="12-hour clock with second precision."
      />
    </div>
  );
}

Step + Business Hours

1000AM

15-minute slots between 9:00 and 18:00.

"use client";

import { useState } from "react";

import { TimePicker } from "@/components/f-ui/time-picker/time-picker";

function businessHoursDisabledTime() {
  return {
    hours: () =>
      Array.from({ length: 24 }, (_, hour) => hour).filter(
        (hour) => hour < 9 || hour > 18,
      ),
  };
}

export function TimePickerStepDemo() {
  const [time, setTime] = useState<Date | null>(
    () => new Date(2026, 4, 21, 10, 0, 0, 0),
  );

  return (
    <div className="max-w-xs w-full">
      <TimePicker
        label="Booking slot"
        value={time}
        onChange={(next) => setTime(next ?? null)}
        step={{ minute: 15 }}
        disabledTime={businessHoursDisabledTime}
        description="15-minute slots between 9:00 and 18:00."
      />
    </div>
  );
}

API Reference

PropTypeDefault
valueDate | null
onChange(time: Date | undefined) => void
formatstringHH:mm or hh:mm a
use12Hoursbooleanfalse
step{ hour?, minute?, second? }1 per unit
disabledTime(current: Date) => { hours?, minutes?, seconds? }
showNowbooleantrue
needConfirmbooleanfalse
changeOnScrollbooleanfalse
minValue / maxValueDatetime-of-day bounds
disabledbooleanfalse
isInvalidbooleanfalse
labelReactNode
description / errorMessageReactNode
localestringi18n provider
classNames{ root?, field?, shell?, … }

value / onChange use Date for compatibility with date pickers; only the clock portion is read or written (normalized to today in the local timezone).

On this page