Skip to content
garn

DateTimePicker

A combined date + time field — a calendar and a time field in one popover, with a single Date value.

Stablev0.4.2added in v0.2.0@garn/ui/date-time-picker
On this page

Default

A default date + time field.

Twelve Hour

A 12-hour format with a custom date-time pattern.

States

Disabled and invalid.

Installation

garn is copy-in — the garn CLI writes this component's source into your project, so you own and can edit it. It resolves any base files and installs the npm dependencies below for you.

terminal
npx garn-ui add date-time-picker
First time? Set up garn in your project
Registry
date-time-picker
Deps
date-fnsclass-variance-authority
Registry deps
utilsdate-iofield-surfaceuse-controllable-stateuse-merged-refdate-pickertime-fieldcalendar

Import

import { DateTimePicker } from "@garn/ui/date-time-picker";

Props

The component's public props and their types.

appearance
Description
No description.
Type
"ghost" | "outline" | "soft"
Default
outline
size
Description
No description.
Type
"lg" | "md" | "sm" | "xl" | "xs"
Default
md
"aria-invalid"
Description
No description.
Type
"false" | "true" | boolean
"aria-label"
Description
No description.
Type
string
"aria-labelledby"
Description
No description.
Type
string
className
Description
No description.
Type
string
clearable
Description
Show a clear button when a value is set.
Type
boolean
Default
true
clearLabel
Description
Accessible name for the clear button.
Type
string
Default
"Clear date and time"
contentClassName
Description
No description.
Type
string
defaultOpen
Description
Whether the popover starts open (uncontrolled).
Type
boolean
Default
false
defaultValue
Description
Initial date-time (uncontrolled).
Type
Date
disabled
Description
Disable the field and block opening.
Type
boolean
Default
false
format
Description
Field format (date + time tokens).
Type
string
Default
"MM/dd/yyyy HH:mm"
id
Description
No description.
Type
string
isDateUnavailable
Description
Per-date disabling (holidays, blackout dates); also blocks typed entry.
Type
(date: Date) => boolean
maxDate
Description
Upper selectable bound (inclusive, day granularity).
Type
Date
minDate
Description
Lower selectable bound (inclusive, day granularity).
Type
Date
name
Description
Name for a hidden input mirroring the value on form submit as a LOCAL date-time `yyyy-MM-dd'T'HH:mm:ss` with no Z/offset (the `<input type="datetime-local">` wire format). Serializing a UTC instant is a consumer decision — convert the `Date` yourself if you truly mean one.
Type
string
onOpenChange
Description
Fires when the popover opens or closes.
Type
(open: boolean) => void
onValueChange
Description
Fires with the committed date-time (or `undefined` when cleared).
Type
(value: Date | undefined) => void
open
Description
Popover open state (controlled).
Type
boolean
placeholder
Description
Field text when nothing is selected (defaults to the `format` string).
Type
string
readOnly
Description
Render read-only — no editing or opening.
Type
boolean
Default
false
ref
Description
No description.
Type
React.Ref<HTMLInputElement>
required
Description
Mark the field required for form validation.
Type
boolean
step
Description
Minutes stepped by the TimeField arrow keys.
Type
number
Default
1
timeFormat
Description
Format for the inner TimeField.
Type
string
Default
"HH:mm"
triggerLabel
Description
Accessible name for the trigger that opens the calendar.
Type
string
Default
"Open calendar"
value
Description
Selected date-time as a single `Date` (controlled).
Type
Date

Plus 3 inherited native <input> attributes.

Styling

Target these data-slots and states, and remap these tokens, to restyle without forking the component.

[data-slot="date-time-picker"][data-slot="date-time-picker-input"][data-slot="date-time-picker-trigger"][data-slot="date-time-picker-clear"]

States

SelectorState
:hoverhover
:focusfocus
:focus-visiblefocus-visible
[aria-invalid=true]invalid
[disabled]disabled
PropertyTokenTier
height--garn-control-h-mdsemantic · air
paddingInline--garn-control-px-mdsemantic · air

When to use

Collect a date and a time together as one timestamp (event start, deadline, appointment).

Reach for something else when

  • Date only (use DatePicker)
  • time only (use TimeField/TimePicker)
  • a range of date-times (compose two, or use DateRangePicker for date-only ranges).

Overview

DateTimePicker is the combined date + time control. One typeable field opens a Popover that pairs the Calendar (date) with a TimeField (time): picking a day keeps the current time, editing the time keeps the day, and the committed value is a single native Date carrying both. A format prop (date + time tokens like MM/dd/yyyy HH:mm) drives the field's display/parse/placeholder; timeFormat drives the inner time field.

It's built on the same field + popover shell as DatePicker (and reuses TimeField), so it matches the rest of the form family. Use DatePicker when there's no time, and TimeField/TimePicker when there's no date.

Guidelines

Pair with a Label and choose a format that shows both parts (date + time). Constrain the date with minDate/maxDate/isDateUnavailable. The field is fully keyboard-operable (ArrowDown opens, Enter commits, Escape closes); inside the popover the calendar and the time field are separate, each operable on its own. Reflect validity with aria-invalid + a described message.

Wire format: local wall time, no Z. With name, the hidden form input posts yyyy-MM-dd'T'HH:mm:ss (the <input type="datetime-local"> wire format) — exactly what the user expressed, never silently converted to UTC. If your backend wants a UTC instant, convert the Date from onValueChange yourself; that's a consumer decision, not the library default.

Best practices

Do
  • Pair with a label; choose a combined format and a matching timeFormat.
  • Constrain the date with min/max/isDateUnavailable.
  • Hold the value in state; read it as one Date.
Don't
  • Don't rely on the placeholder as the label.
  • Don't split date and time into two separate values — the point is one Date.

Content guidelines

  • Label with what the timestamp is for ("Starts at").
  • Use a format that reads unambiguously ("MMM d, yyyy · h:mm a").

Troubleshooting

Picking a date resets the time to midnight.

Cause. There was no time yet, so the first date pick defaults the time to 00:00.

Fix. Set the time via the inner time field (it's preserved on subsequent date picks).

Typed date-time snaps back.

Cause. The text didn't match format (which includes the time tokens).

Fix. Type in the exact format shown as the placeholder.

Accessibility

Role
textbox
ARIA APG
date-picker-dialog
Focus
Field focus surface as the date pickers; on open, focus enters the calendar; the time field is a separate tab stop in the popover.

Accessibility requirements

warn

Give every date-time picker an accessible label.

when no associated <label htmlFor>, aria-label, or aria-labelledby

Keyboard

ArrowDownOpen the calendar + time popover.
EnterCommit the typed date-time.
EscapeClose the popover.
ArrowUpArrowDownStep the time (inside the time field) by `step` minutes.
A1.3.1Info and RelationshipsA2.1.1KeyboardAA2.4.7Focus VisibleA4.1.2Name, Role, Value
  • Give the field an accessible label; the format-pattern placeholder is not a label.
  • The popover pairs a labeled calendar grid with a labeled time field; each is independently operable.
  • Reflect validation with aria-invalid + a described message.