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

Date Time Picker

Inputs

Date Time Picker React component for Tailwind CSS v4

Date Range PickerDuration Picker
Live props
Content
label
description
error
placeholder
name
sheetTitle
locale
State
required
invalid
disabled
readOnly
defaultOpen
Behavior
minuteStep
startTime
endTime
disablePast
presets
timeZone
hourCycle
clearable
weekStartsOn-1
responsive
Appearance
showTimeZone
tone
color
size
variant
radius
Layout
inline
side
align
Code
import { DateTimePicker } from "@/components/ui/date-time-picker"

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

Date Time Picker React component for Tailwind CSS v4

v1.0.1·Updated in 1.7.0

A React date time picker: a calendar beside a column of free time slots, presets and a time zone label in one field, a sheet on phones.

Click the field (or press Enter, Space or ArrowDown on it) to open a panel under it: an optional row of preset chips (In 1 hour, Tomorrow 9:00, Monday 9:00 or your own), the calendar and, beside it, a column of time slots for the chosen day. Clicking a day shows its times and keeps the time you had if that slot is free that day; otherwise the first free slot is highlighted and the field shows the day with --:-- until you click a time. Clicking a time sets the value at once and the panel stays open until Done, Enter, Escape or a click outside; the selected slot's fill glides between slots of the same day. Taken slots are struck through and can not be picked, slots from getTimeSlots can carry a note ("2 locuri"), a promise shows pulsing placeholders, and a failed request shows a retry button. A preset sets day and time together and closes. The footer shows the time zone (a select when timeZones is set: switching it keeps the moment and moves the wall clock) and Done; the inline panel shows a summary of the pick there instead. Keyboard: focus lands on the selected day (else today); arrows move by day and week, PageUp / PageDown by month, Enter picks the day; Tab moves to the time list, where the arrows move one slot, Home / End jump to the ends, PageUp / PageDown move four slots, typing digits jumps ("14" to 14:00, "9" to 09:00), Space picks and Enter picks and closes; Tab reaches the zone select and Done; Escape closes and returns focus to the field. On a phone the field opens a bottom sheet instead: presets and times are horizontal chip rails with 44px chips (the chosen time scrolls to the center), the calendar is full width, and the choice is a draft that the big Done button commits; dragging the sheet away keeps the old value. Below 640px the inline panel (and a popover with responsive off) puts the times in a grid under the calendar instead of a column beside it. Clear (clearable) empties the field.

Date 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 date-time-picker, together with Button, Calendar, Drawer, Field, Select and 8 more and the npm packages @radix-ui/react-popover, date-fns, 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 date-time-picker

Date 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 DateTimePicker from @/components/ui/date-time-picker.

import { useState } from "react"
import { DateTimePicker, buildTimeSlots, type DateTimeSlot } from "@/components/ui/date-time-picker"

// Liam Walker's free slots: weekdays 09:00 to 17:00, 10:00 and 13:30 taken
function freeSlots(day: Date): DateTimeSlot[] {
  if (day.getDay() === 0 || day.getDay() === 6) return []
  return buildTimeSlots({ step: 30, start: "09:00", end: "17:00" }).map((time) => ({
    time,
    disabled: time === "10:00" || time === "13:30",
  }))
}

export function ScheduleDemo() {
  const [value, setValue] = useState<Date | null>(null)
  return (
    <DateTimePicker
      label="Date and time"
      placeholder="Pick a free slot"
      value={value}
      onValueChange={setValue}
      getTimeSlots={freeSlots}
      disablePast
      presets
      timeZone="America/New_York"
      locale="en-US"
      name="appointment"
      labels={{ noSlots: "No free slots that day", tomorrowMorning: "Tomorrow, 9 AM" }}
    />
  )
}

Props

  • labeldefault ""
    ReactNode

    The field label. Plain text also titles the phone sheet.

  • descriptiondefault ""
    ReactNode

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

  • errordefault ""
    ReactNode

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

  • requireddefault false
    boolean

    The required mark after the label and aria-required on the trigger.

  • invaliddefault false
    boolean

    The invalid look and aria-invalid without a message.

  • disableddefault false
    boolean

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

  • readOnlydefault false
    boolean

    Shows the value but never opens the panel (inline: picks are ignored).

  • value
    Date | null

    The moment, controlled (an absolute Date; null is empty). A value off the step grid (16:37) shows as is and as an extra slot in the list.

  • defaultValuedefault "null"
    Date | null

    The starting moment while uncontrolled. A form reset returns to it.

  • onValueChange
    (value: Date | null) => void

    Called when a slot or a preset is picked, when a new day keeps the chosen time, on Clear, and on the sheet's Done.

  • minuteStepdefault 15
    number

    Minutes between slots when getTimeSlots is not set; also rounds the In 1 hour preset.

  • startTimedefault "00:00"
    string

    The first slot of the day, "HH:mm" ("08:00" for business hours).

  • endTimedefault "23:59"
    string

    The last possible slot of the day, "HH:mm".

  • getTimeSlots
    (day: Date) => a list of "HH:mm" strings or DateTimeSlot objects, or a Promise of that list

    Availability per day (a local midnight Date for the wall day in the zone), replacing the step list. Items are "HH:mm" or { time, disabled?, label? }. A promise shows placeholder slots, a rejection shows noSlots with a retry. Answers are cached per function and day, so pass a stable function.

  • disabledTimes
    (slot: Date) => boolean

    Disables single slots by their absolute moment.

  • min
    Date

    Days and slots before are disabled (the day of min stays open for its later slots).

  • max
    Date

    Days and slots after are disabled. Ignored when it is before min.

  • disablePastdefault false
    boolean

    Shorthand for min = now: past days and today's past slots are disabled.

  • disabledDays
    DateMatcher or DateMatcher[] (DateMatcher: Date, Date[], { from, to }, { before }, { after }, { dayOfWeek } or (date) => boolean)

    Days the calendar disables: a date, a list, { from, to }, { before }, { after }, { dayOfWeek: [0, 6] } or a test.

  • presetsdefault false
    boolean | DateTimePreset[]

    true: In 1 hour (rounded up to the step), Tomorrow 9:00 and Monday 9:00. Or your own { label, value, key? } list. A preset sets day and time and closes the panel.

  • timeZonedefault ""
    string

    The IANA zone the wall times are picked in ("America/New_York"); the calendar and slots show its clock and DST gaps are disabled. Empty uses the device zone; an unknown zone falls back to it with a warning.

  • showTimeZonedefault true
    boolean

    The zone label in the footer.

  • timeZones
    string[]

    Makes the zone label a select to switch zones; the moment stays, the wall clock moves.

  • onTimeZoneChange
    (zone: string) => void

    Called when a zone is picked from the select.

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

    12 or 24 hour slots and trigger text; auto follows the locale.

  • displayFormatdefault "{ weekday: "short", day: "numeric", month: "short", year: "numeric", hour: "2-digit", minute: "2-digit" }"
    Intl.DateTimeFormatOptions

    How the trigger writes the value ("Thu, Sep 24, 2026, 4:40 PM"). While only a day is picked its date part is shown with --:--.

  • placeholderdefault "Pick date and time"
    string

    The trigger text while empty.

  • clearabledefault false
    boolean

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

  • inlinedefault false
    boolean

    Renders the panel in the page without a trigger.

  • weekStartsOndefault -1
    0 | 1 | 2 | 3 | 4 | 5 | 6 | -1

    The calendar's first weekday (0 Sunday to 6 Saturday); -1 uses the locale's.

  • calendarProps
    Partial<CalendarProps>

    Anything else for the calendar: size, radius, events, showWeekNumbers, labels.

  • now
    Date | number

    What now means for presets and disablePast. Default: the clock, ticking each minute (null until hydrated).

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

    The selected day, slot and preset, and the Done button.

  • colordefault ""
    string

    Any CSS color; replaces the tone.

  • icon
    ReactNode

    The trigger's leading icon. Default: CalendarClock.

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

    The field box: 32 / 40 / 48px (md grows to 44px on touch).

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

    The field box look.

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

    The field box corners.

  • namedefault ""
    string

    Submits value.toISOString() through a hidden input.

  • defaultOpendefault false
    boolean

    Starts with the panel (or the sheet) open.

  • open
    boolean

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

  • onOpenChange
    (open: boolean) => void

    Called when the panel opens or closes.

  • responsivedefault true
    boolean

    Below 768px the panel opens as a bottom sheet with chip rails and a draft committed by Done.

  • sheetTitledefault ""
    string

    The sheet title; empty uses the label's text, else labels.dialog.

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

    Where the popover opens (it flips when there is no room).

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

    The popover's alignment to the field.

  • container
    HTMLElement | null

    Where the popover or sheet portals to.

  • localedefault ""
    string

    Month and weekday names, the first weekday, the hour cycle and the zone names. Empty uses the nearest LocaleProvider or UIProvider, else the page's language.

  • labels
    Partial<DateTimePickerLabels>

    The strings: time "Time", timeFor(day) "Time for {day}", noSlots "No free times on this day", pickDay, loading, retry "Try again", timeZone "Time zone", done "Done", clear "Clear", dialog "Choose date and time", presets "Quick picks", inOneHour "In 1 hour", tomorrowMorning "Tomorrow 9:00", nextMonday "Monday 9:00", selected(text), noTime "--:--". DEFAULT_DATE_TIME_PICKER_LABELS holds the defaults.

  • fieldProps
    Partial<FieldProps>

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

  • id
    string

    The trigger's id; generated when left out.

  • classNames
    Partial<Record<"field" | "label" | "description" | "error" | "counter" | "box" | "trigger" | "icon" | "value" | "clear" | "chevron" | "content" | "presets" | "preset" | "body" | "calendar" | "times" | "slot" | "footer" | "zone" | "done", string>>

    Extra classes for the inner parts.

  • className
    string

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

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

    The trigger button. Other button props (aria-*, data-*, tabIndex) land on it too.

Parts

  • DateTimePickerPanel

    component

    The panel alone (presets, calendar, times, footer) with the same options plus value / defaultValue / onValueChange, day / onDayChange (the day on show before a time is picked, "YYYY-MM-DD"), layout ("popover" | "sheet" | "inline"), onDone and onPresetPick. Props: DateTimePickerPanelProps.

  • DEFAULT_DATE_TIME_PICKER_LABELS

    constant

    The English strings, to spread under a translation. Type: DateTimePickerLabels.

  • zonedTimeToDate

    function

    A wall time in a zone to the absolute moment, DST safe: zonedTimeToDate("2026-09-24", "16:40", "America/New_York"). Repeated hours give the first occurrence. Signature: (dateISO: string, time: string, timeZone?: string) => Date.

  • getZonedParts

    function

    The wall clock of a moment in a zone: { date: "2026-09-24", time: "16:40" }. Signature: (date: Date, timeZone?: string) => { date: string; time: string }.

  • formatTimeZoneLabel

    function

    The zone's name and its offset at that date: "Romania Time (UTC+03:00)". Signature: (timeZone: string, date: Date, locale?: string) => string.

  • buildTimeSlots

    function

    The "HH:mm" list every step minutes from start to end. Signature: ({ step?, start?, end? }) => string[].

  • isValidTimeZone

    function

    Whether the runtime knows an IANA zone ("" counts as the device zone). Signature: (timeZone: string) => boolean.

Examples

Send later

Presets (in 1 hour, tomorrow 9:00, Monday 9:00), 30 minute steps, clearable, brand tone.

Time zones

Pick in New York, switch the zone from the footer select, read the same moment in London and Bucharest.

Clinic appointments

getTimeSlots returns a promise per day (600 ms): skeleton slots, then free chairs as labels.

Inline panel

No trigger: the calendar and an 8:00 AM to 8:00 PM time column inside the page.

Phone sheet

The sheet layout: presets and times as chip rails under a full width calendar, Done commits.

DST day

March 8, 2026 in New York: the wall times the clock skips are disabled.

Locales

The same value in en-US (12h, New York time) and ro-RO (24h, Bucharest time).

Dependencies

@radix-ui/react-popoverdate-fnsmotionlucide-reactbuttoncalendardrawerfieldselectdate-utilsrelative-timeuse-controllable-stateuse-localeuse-media-querymotiontonesui-config

Files

  • components/ui/date-time-picker.tsx

FAQ

Is the React Date Time Picker component free?

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

How do I install Date Time Picker?

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

How does Date Time Picker behave on phones?

Below 768px the panel opens as a bottom sheet with chip rails and a draft committed by Done. The responsive prop controls this and is on by default.

See also

Date PickerTime PickerCalendarDate Range PickerTimezone SelectScheduler

Used in

Booking WidgetAppointment Booking Page

More React input components

Email InputEmoji PickerFile UploadFilter BuilderInputInput Group
All 40 React input components

Articles using this component

  • 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

PreviousDate Range PickerNext Duration Picker

Schedule the demo

Tech Solutions Inc. · Chicago

30 min

Liam Walker

Sales · free slots from his calendar

Google Meet · link sent after confirming

value: "2026-09-25T15:30:00.000Z"

15 min · 09:00 to 17:00 · America/New_York · auto · on a phone: a “Date and time” sheet · bottom / start

en-US · empty: “Pick date and time”

↓Down arrow calendar TabTab times 141 4 2:00 PM EnterEnter pick