Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
Components
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCountry SelectCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerEmail InputEmoji PickerFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime PickerTimezone SelectTree Select
Forms
Address InputBooking WidgetCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text EditorSchema Builder
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverShare DialogSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSetup ChecklistSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadar chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselCSV ViewerDOCX EditorDOCX ViewerExcel ViewerFile SystemImage compareImage CropperImage GalleryImage LightboxLocation MapPDF ViewerPowerPoint ViewerQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadChat WidgetConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Background PatternGravity LettersMarqueeTerminalText AnimateText Rotate
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Marketing Sections
App Download SectionBlog Cards SectionCTA SectionFAQ SectionFeature GridFeature ShowcaseHero SectionIntegrations SectionLogo CloudMarketing FooterMarketing HeaderMarketing SectionNewsletter SectionPricing SectionStats SectionSteps SectionTeam SectionTestimonials SectionTimeline SectionVideo Section
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAPI Keys PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowOrder History PagePost Listing WizardPricing PageProfile PageRegister PageSettings PageUsers Admin Page
Templates
About PageAcademy PageAccessibility PageAffiliate PageAgency PageAI Product PageAppointment Booking PageBlog PageBlog Post PageBug Bounty PageCareers PageChangelog PageCommunity PageCompare PageContact PageCustomer Story PageCustomers PageDemo Booking PageDevelopers PageDocs Home PageDownload PageEnterprise PageEvents PageExperts PageFAQ PageFeature PageGlossary PageHelp Article PageHelp Center PageHome Services PageImpact PageIntegration Detail PageIntegrations PageJob Detail PageLanding PageLaunch PageLegal PageManifesto PagePortfolio PagePress PageProduct Tour PagePromo PageReal Estate PageReferral PageResearch Report PageRestaurant PageRoadmap PageROI Calculator PageSecurity PageSolutions PageStartups PageStatus PageStorefront PageSwitch PageTemplate Gallery PageTravel PageWaitlist PageYear in Review Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryTheme

Inputs

Time Picker

Inputs

Time Picker React component for Tailwind CSS v4

TextareaTimezone Select
Live props
Field
label
description
error
required
invalid
disabled
readOnly
Content
defaultValue
placeholder
locale
sheetTitle
name
Behavior
hourCycle
minuteStep
secondStep
strictStep
withSeconds
min
max
clearable
showNow
closeOnSelect
responsive
State
defaultOpen
Appearance
size
variant
radius
tone
color
showColumnLabels
Layout
side
align
Code
import { TimePicker } from "@/components/ui/time-picker"

<TimePicker />
  1. Home
  2. Components
  3. Inputs
  4. Time Picker
Pro

Time Picker React component for Tailwind CSS v4

v1.0.0·Added in 1.0.0

A React time picker to type a time or pick it from columns, with 12 / 24 hours by locale, minute steps, limits and wheels on phones.

Type a time the way you would say it: "1430", "14.30", "9", "2:30pm" or "2p" (a colon appears after the first two digits). It is read on blur and on Enter; text that does not parse turns the field red while you type and goes back to the last good time on blur, a time outside min / max moves to the nearest limit, and with strictStep the minutes snap to the step. ArrowUp / ArrowDown step the minutes by minuteStep and Shift+ArrowUp / ArrowDown step an hour, committing at once and skipping blocked times. Clicking the field or the clock button opens a panel of columns (hours, minutes, seconds, AM / PM) with the chosen time centered; clicking an option sets it at once, and a minute (or second) click closes the panel. Alt+ArrowDown (or Enter on the clock button) opens it with focus on the hour: ArrowUp / ArrowDown move and pick, PageUp / PageDown jump five, Home / End go to the ends, ArrowLeft / ArrowRight switch columns, Enter confirms and closes, Escape closes and returns to the field. On a phone the field never raises the keyboard: a tap opens a bottom sheet with wheels that snap like iOS, tick under the thumb where the phone supports vibration, and commit with Done; dragging the sheet away keeps the old time. The wheels also take arrows, PageUp / PageDown, Home and End.

Time Picker is a Pro React component in the React input components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add time-picker, together with Button, Drawer, Field, useControllableState, useLocale and 5 more and the npm packages @radix-ui/react-popover, motion and lucide-react.

Add to a project

Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup

npx wingo-ui@latest add time-picker

Time Picker is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans

Usage

Import TimePicker from @/components/ui/time-picker.

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

const lunchBreak = (time: string) => time >= "13:00" && time < "14:00"

export function PickupTime() {
  const [time, setTime] = useState("11:30")
  return (
    <TimePicker
      label="Pickup time"
      description="The store is open from 9 AM to 6 PM."
      value={time}
      onValueChange={setTime}
      locale="en-US"
      min="09:00"
      max="18:00"
      minuteStep={15}
      disabledTimes={lunchBreak}
      clearable
      showNow
      name="pickup_time"
    />
  )
}

Props

  • labeldefault ""
    ReactNode

    The field label, linked to the input. Plain text also becomes the title of the phone sheet.

  • descriptiondefault ""
    ReactNode

    Helper text under the field, linked with aria-describedby.

  • errordefault ""
    ReactNode

    Replaces the description with an animated error and marks the field invalid.

  • requireddefault false
    boolean

    The red mark after the label and required on the input.

  • invaliddefault false
    boolean

    The invalid look and aria-invalid without a message.

  • disableddefault false
    boolean

    Dims the field, blocks typing and never opens the panel; the hidden input is not submitted.

  • readOnlydefault false
    boolean

    The time stays visible and selectable but can not change: no typing, no panel, no clear button.

  • defaultValuedefault ""
    string

    The starting time while uncontrolled, as "HH:mm" in 24 hours ("HH:mm:ss" with seconds), like <input type="time">. A form reset returns to it.

  • value
    string

    The time, controlled: "HH:mm" in 24 hours whatever the display ("" is empty). A value off the step shows as given and is highlighted in no column.

  • onValueChange
    (value: string) => void

    Called with the new "HH:mm" (or "") after a typed time is read, an arrow step, a column pick, Now, Clear or the sheet's Done.

  • placeholderdefault ""
    string

    Empty uses "--:--" for 24 hours and "--:-- --" for 12 hours (with seconds, one more group).

  • localedefault ""
    string

    How the time is written and which hour cycle auto picks: en-US 2:30 PM, en-GB 14:30. Empty uses the nearest LocaleProvider, else the page's language.

  • hourCycledefault "auto"
    "auto" | "h12" | "h23"

    12 or 24 hours. auto follows the locale. The value is always 24 hours underneath, so switching keeps the time; midnight shows as 12:00 AM.

  • minuteStepdefault "5"
    number

    The minutes offered in the columns and wheels and the ArrowUp / ArrowDown step. Typed minutes can be anything unless strictStep.

  • secondStepdefault "5"
    number

    The seconds offered with withSeconds.

  • strictStepdefault false
    boolean

    Typed minutes (and seconds) snap to the nearest step on blur: 14:37 becomes 14:35 with a 5 minute step.

  • withSecondsdefault false
    boolean

    Values become "HH:mm:ss" and a seconds column (and wheel) appears.

  • mindefault ""
    string

    The earliest time ("09:00"). Earlier options are disabled and typed times are moved up to it. A min later than max is an overnight window (22:00 to 06:00) that wraps past midnight.

  • maxdefault ""
    string

    The latest time ("18:00"). Later options are disabled and typed times are moved down to it.

  • disabledTimes
    (time: string) => boolean

    Blocks single times, called with "HH:mm" (or "HH:mm:ss"): a lunch break, booked slots. Blocked options are disabled, hours with nothing left grey out, arrow steps skip them and a typed one moves to the nearest free slot.

  • clearabledefault false
    boolean

    A clear button in the field while it has a time (and a Clear button in the phone sheet).

  • showNowdefault false
    boolean

    A Now button under the columns (and in the sheet) that sets the current time, rounded to the step and moved to the nearest allowed one.

  • closeOnSelectdefault true
    boolean

    Desktop: a click in the minutes column (the seconds column with withSeconds) closes the panel. Hours and AM / PM never close it. The phone sheet always waits for Done.

  • responsivedefault true
    boolean

    Below 768px the field opens a bottom sheet with wheels instead of the panel. Off, phones get the panel too.

  • defaultOpendefault false
    boolean

    Starts with the panel (or the sheet) open. Pass open and onOpenChange to control it.

  • open
    boolean

    Whether the panel or sheet is open, controlled. It never opens while disabled or readOnly.

  • onOpenChange
    (open: boolean) => void

    Called when the panel or sheet opens or closes.

  • sizedefault "md"
    "sm" | "md" | "lg"

    Field heights of 32, 40 and 48px (md is 44px on touch screens), with matching text.

  • variantdefault "soft"
    "soft" | "outline" | "underline" | "ghost"

    The field box: a tinted fill, a border, a bottom line that grows on focus, or nothing until hover.

  • radiusdefault "md"
    "none" | "sm" | "md" | "lg" | "full"

    Corner radius of the field, scaled per size like the button. Ignored by underline.

  • tonedefault "neutral"
    "neutral" | "brand" | "success" | "warning" | "danger" | "info"

    The fill of the selected options and the sheet's Done button.

  • colordefault ""
    string

    Any CSS color for the selection; replaces the tone. Text on it turns black or white by lightness.

  • showColumnLabelsdefault false
    boolean

    Small titles above the desktop columns (Hours, Minutes...) from labels.

  • icon
    ReactNode

    The trigger button's icon; a clock by default.

  • sheetTitledefault "Select time"
    string

    The phone sheet's title when the label is not plain text (or missing).

  • labels
    Partial<{ now; done; clear; open; dialog; hours; minutes; seconds; period; format: string }>

    Every visible and spoken string, merged over the English defaults ("Now", "Done", "Clear", "Choose time", "Hours"...). format is the screen reader hint after the field, with {format} replaced by the pattern ("Format HH:mm").

  • namedefault ""
    string

    Renders a hidden input with this name and the "HH:mm" value, for native forms. The visible input has no name.

  • sidedefault "bottom"
    "top" | "right" | "bottom" | "left"

    Where the panel opens; it flips when there is no room.

  • aligndefault "start"
    "start" | "center" | "end"

    How the panel lines up with the field.

  • container
    HTMLElement | null

    Portal target of the panel and the sheet; defaults to document.body.

  • fieldProps
    Partial<FieldProps>

    Anything else for the Field wrapper: optional, labelAction, orientation, labelWidth, hideLabel.

  • id
    string

    The input's id; generated when left out.

  • classNames
    Partial<Record<"field" | "label" | "description" | "error" | "counter" | "box" | "input" | "trigger" | "clear" | "content" | "column" | "option" | "wheel" | "band" | "footer", string>>

    Extra classes for the inner parts. content is the panel (or the sheet), band the shared highlight behind the wheels.

  • className
    string

    Extra classes for the outermost element: the Field when there is a label or message, else the box.

  • ref
    Ref<HTMLInputElement> (a useRef object or an (el) => void callback)

    The text input. Other props (onBlur, autoFocus, aria-*, data-*) land on it too.

Parts

  • TimePickerWheel

    component

    The snapping wheel from the phone sheet, on its own: rows turn away from the center like iOS. Use it for durations, alarms or any short list of values.

    • options*
      TimePickerWheelOption[]

      The rows: { value, label, disabled? }.

    • value
      string

      The selected value, controlled.

    • defaultValue
      string

      The starting value while uncontrolled.

    • onValueChange
      (value: string) => void

      Fires once the wheel comes to rest on a new row.

    • label*
      string

      The listbox's accessible name, like "Hours".

    • banddefault true
      boolean

      The rounded band behind the centered row; turn it off when several wheels share one band.

    • hapticsdefault true
      boolean

      A short vibration tick as each row crosses the center, where the phone supports it.

    • disableddefault false
      boolean

      Blocks scrolling and keys.

    • classNames
      Partial<Record<"list" | "item" | "band", string>>

      Extra classes for the list, each row and the band.

  • TimePickerWheelOption

    type

    One wheel row: { value, label, disabled? }.

  • TimePickerProps

    type

    The props of TimePicker, for wrappers that pass them through.

Examples

12 and 24 hours

The same value in en-US (2:30 PM, with an AM / PM column) and en-GB (14:30). The value is always 24 hours.

Minute steps

1, 5, 15 and 30 minute columns; the last two snap typed minutes with strictStep.

With seconds

withSeconds adds a seconds column and wheel, here every 5 seconds, with column titles and the brand tone.

Opening hours and a lunch break

min / max grey out the rest of the day, disabledTimes blocks 13:00 to 14:00, and Now picks the nearest free slot.

Typing

Type 1430, 14.30, 9 or 2:30pm: a colon appears after two digits and the time is read on blur or Enter.

Phone wheels

TimePickerWheel on its own: scroll, drag or use the arrows. Open any picker at 390px to get the full sheet.

Sizes and variants

sm, md and lg in the four field boxes.

Overnight window

min after max wraps past midnight: 22:00 to 06:00, with a custom color, full radius and the outline box.

Dependencies

@radix-ui/react-popovermotionlucide-reactbuttondrawerfielduse-controllable-stateuse-localeuse-media-querydate-utilsmotiontonesui-config

Files

  • components/ui/time-picker.tsx

FAQ

Is the React Time Picker component free?

The preview, props and docs are free; copying the source needs a Pro plan. See plans.

How do I install Time Picker?

Run npx wingo-ui@latest add time-picker. It also adds the Wingo UI items it builds on (Button, Drawer, Field, useControllableState, useLocale and 5 more) and the npm packages @radix-ui/react-popover, motion and lucide-react. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/time-picker.

How does Time Picker behave on phones?

Below 768px the field opens a bottom sheet with wheels instead of the panel. Off, phones get the panel too. The responsive prop controls this and is on by default.

See also

CalendarFieldDrawerDate utilslibInput

Used in

Cron PickerDate PickerSchedulerSettings PageRestaurant Page

More React input components

Timezone SelectTree SelectDuration PickerOTP InputAvatar UploadColor Picker
All 40 React input components

Articles using this component

  • React Appointment Booking That Gets Time Zones Right

    Build it: screens and apps · Nov 6, 2026 · 9 min read

  • Shadcn Date Time Picker with Time Zones and Presets

    Component guides · Nov 5, 2026 · 9 min read

  • React Time Picker: Typed Input, 12 or 24 Hours by Locale

    Component guides · Oct 19, 2026 · 8 min read

  • React Wheel Picker: iOS Wheels for Times and Dates

    Mobile UI in React · Oct 9, 2026 · 8 min read

PreviousTextareaNext Timezone Select

Store pickup

Order #48213 · 3 items

Ready
Thursday, Sep 24, 2026Hudson Yards, New York · 09:00–18:00
Format hh:mm AM/PM

value: "11:30" · step 5m

On a phone: a wheel sheet “Select time” · the minute closes the panel

↑↓Up arrow Down arrow ±5 min ShiftShift ±1 h Alt↓Alt Down arrow panel