Skip to content
Components, switch product

Date-time picker

<gui-date-time-picker> is a date and time field with a popup. The user types the date and time part by part, as in a Date-time input, or picks them from a Date-time calendar: a calendar with a time picker on top. Its value is a local ISO date-time, such as 2026-10-05T18:45:00.

Code of the example above
DateTimePickerBasic.tsx
import { GuiDateTimePicker } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function DateTimePickerBasic() {
return <GuiDateTimePicker label="Appointment" />;
}

The element’s entry point is @golemui/gui-components/date-time-picker, which registers <gui-date-time-picker> and the elements it is made of: <gui-date-time>, <gui-date-time-calendar> and its <gui-time-picker>. To install the package and load the stylesheet, see Installation.

The @golemui/gui-components/react components have typed props and an on* prop for each event. React 18 or later.

AppointmentField.tsx
import { useState } from 'react';
import { GuiDateTimePicker } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function AppointmentField() {
const [appointment, setAppointment] = useState('');
return (
<GuiDateTimePicker
label="Appointment"
name="appointment"
value={appointment}
onGuiChange={(event) => setAppointment(event.detail.value ?? '')}
/>
);
}

Properties

Property Type Default Description
icon string '' Icon class name shown inside the control, for example from an icon font.
toggleAriaLabel attribute toggle-aria-label string – Accessible name of the button that opens the popup. An empty value keeps the default.
dayAriaLabel attribute day-aria-label string – Accessible name of the day part. An empty value keeps the default.
monthAriaLabel attribute month-aria-label string – Accessible name of the month part. An empty value keeps the default.
yearAriaLabel attribute year-aria-label string – Accessible name of the year part. An empty value keeps the default.
hourAriaLabel attribute hour-aria-label string – Accessible name of the hour part. An empty value keeps the default.
minuteAriaLabel attribute minute-aria-label string – Accessible name of the minute part. An empty value keeps the default.
dayPeriodAriaLabel attribute day-period-aria-label string – Accessible name of the AM/PM part. An empty value keeps the default.
showErrors attribute show-errors boolean true Whether the element renders its own error list. Elements that embed it turn it off and show the errors themselves.
localeId attribute locale-id string – BCP 47 locale for formatting and parsing, such as en-US or es.
value string – The date and time, as an ISO date-time (YYYY-MM-DDTHH:mm:ss).
prevMonthIcon attribute prev-month-icon string '' Icon class name of the previous-month button.
nextMonthIcon attribute next-month-icon string '' Icon class name of the next-month button.
prevMonthAriaLabel attribute prev-month-aria-label string – Accessible name of the previous-month button. An empty value keeps the default.
nextMonthAriaLabel attribute next-month-aria-label string – Accessible name of the next-month button. An empty value keeps the default.
selectYearAriaLabel attribute select-year-aria-label string – Accessible name of the button that opens the year grid. An empty value keeps the default.
yearGridAriaLabel attribute year-grid-aria-label string – Accessible name of the year grid. An empty value keeps the default.
dayFormat attribute day-format 'numeric' | '2-digit' – How day numbers are written.
weekdayFormat attribute weekday-format 'short' | 'long' | 'narrow' – How weekday names are written in the header.
monthFormat attribute month-format 'numeric' | '2-digit' | 'long' | 'short' | 'narrow' – How the month is written in the header.
minDate attribute min-date string – Earliest selectable date, as an ISO date (YYYY-MM-DD).
maxDate attribute max-date string – Latest selectable date, as an ISO date (YYYY-MM-DD).
disabledRanges attribute disabled-ranges DateRange[] – Dates that cannot be picked, as { start, end } ISO date ranges.
numberOfMonths attribute number-of-months number – Number of months shown side by side.
hourFormat attribute hour-format HourFormat – 12- or 24-hour clock. Defaults to the locale's.
minuteStep attribute minute-step number – Minutes between the times offered in the list.
minTime attribute min-time string – Earliest selectable time, as an ISO time (HH:mm:ss).
maxTime attribute max-time string – Latest selectable time, as an ISO time (HH:mm:ss).
disabledTimeRanges attribute disabled-time-ranges DisabledTimeRange[] – Times that cannot be picked, optionally only on a date or on some weekdays.
allowCustomTime attribute allow-custom-time boolean false Allows typing any time, not only picking one from the list.
timeLabel attribute time-label string – Label of the time in the calendar. An empty value keeps the default.
invalidDateMessage attribute invalid-date-message string – Error for a complete but impossible date, such as February 31.
minDateMessage attribute min-date-message string – Error for a date before minDate.
maxDateMessage attribute max-date-message string – Error for a date after maxDate.
disabledDateRangeMessage attribute disabled-date-range-message string – Error for a date inside disabledRanges.
minTimeMessage attribute min-time-message string – Error for a time before minTime.
maxTimeMessage attribute max-time-message string – Error for a time after maxTime.
disabledTimeRangeMessage attribute disabled-time-range-message string – Error for a time inside disabledTimeRanges.
noAvailableTimesMessage attribute no-available-times-message string – Text shown when no time can be picked.
incompleteMessage attribute incomplete-message string – Error when focus leaves a partly filled value.
uid string – The id of the element's control. The ids of its label, hint and errors derive from it. Optional: when it is not set, the element generates one from its tag name and a page-wide counter, e.g. gui-textinput-3. Generated ids depend on creation order, so a server-rendered element must set uid or its ids will not match after hydration.

Form control properties

Every field has these, from the base class of the form controls.

Property Type Default Description
name string – The name the value is submitted under. Without it the element stays out of its form. Reflected, like a native control's: the form reads a single value's name from the attribute.
label string – The visible label, which also names the control for assistive technology. Without one the control has no accessible name; the date, time, range, tags and file fields warn about it in development builds.
hint string – Help text shown under the label and announced as the control's description.
errors string[] [] Error messages to show, typically from your own validation. They show as soon as they are set, unless touched is false.
touched boolean – Whether the user has interacted with the control. Leave it unset unless you validate on interaction: false holds errors back until it becomes true.
required boolean false The control needs a value: it is marked as required and, with a name, blocks submission.
readOnly attribute readonly boolean – The value can be read and focused but not changed.
disabled boolean – Disabled by its own attribute or by a disabled <fieldset> around it. Setting the property sets the attribute, like a native control's.
form read-only, no attribute HTMLFormElement | null – The <form> the element belongs to, if any.
validity read-only, no attribute ValidityState – The element's validity, like a native control's.
validationMessage read-only, no attribute string – The message the browser shows for an invalid value.

Methods

Method Description
checkValidity(): boolean –
reportValidity(): boolean –

Events

Every event bubbles and is composed, so you can listen on any ancestor.

Event Description
gui-input The user changed the value. detail.value is the new value.
gui-change The user committed the value. detail.value is the committed value.
gui-blur Focus left the control.
gui-input-error The element rejected what the user entered, such as an impossible date or a value out of bounds. detail.message is the error; show it through errors.

CSS custom properties

Property Default Description
--gui-calendar-width – Width of one month.
--gui-calendar-day-button-size – Size of each day.
--gui-calendar-change-month-button-width – Width of the previous- and next-month buttons.
--gui-calendar-change-month-button-height – Height of the previous- and next-month buttons.
--gui-calendar-year-button-width – Width of each year in the year grid.
--gui-calendar-year-button-height – Height of each year in the year grid.
--gui-calendar-year-grid-height – Height of the year grid.
--gui-calendar-time-grid-height – Height of the time grid.
--gui-calendar-time-button-height – Height of each time in the grid.

value takes a local ISO date-time, YYYY-MM-DDTHH:mm:ss, with no time zone. The popup opens when a part of the field gets focus, or from the button at the end of the field, on the month of the value.

The value needs a day and a time. Pick a day, then a time: the time picker at the top of the popup opens its list in place of the days, and picking a time sets the value, closes the popup and returns focus to the field. Once there is a value, picking another day keeps its time and changes the value at once, with the popup still open. Escape, a click outside or moving focus out of the element closes the popup too.

Each change of the value fires gui-input and then gui-change, with the date-time in event.detail.value.

Value: 2026-11-30T10:00:00

Code of the example above
DateTimePickerValue.tsx
import { useState } from 'react';
import { GuiDateTimePicker } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function DateTimePickerValue() {
const [value, setValue] = useState<string | undefined>('2026-11-30T10:00:00');
return (
<>
<GuiDateTimePicker
id="date-time-picker-call"
label="Call with Grace"
value={value}
onGuiChange={(event) => setValue(event.detail.value)}
/>
<p role="status">Value: {value ?? 'empty'}</p>
</>
);
}

The time picker in the popup takes minute-step, the minutes between its times, 30 by default, and min-time and max-time, inclusive ISO times that apply on every day. time-label names it: Time by default. allow-custom-time lets the user type any time there, as in the Time picker. locale-id and hour-format set the order of the parts and the clock, as on the Date-time input.

Code of the example above
DateTimePickerTimes.tsx
import { GuiDateTimePicker } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function DateTimePickerTimes() {
return (
<GuiDateTimePicker
label="Pickup"
timeLabel="Pickup time"
minuteStep={15}
minTime="08:00:00"
maxTime="12:00:00"
value="2026-12-14T09:00:00"
/>
);
}

min-date, max-date and disabled-ranges work as on the Date picker: those days can’t be picked. disabled-time-ranges takes a JSON list of { "start", "end" } ISO times, both ends included, that can’t be picked. Scope an entry to some days with weekdays, a list of day numbers from 0 for Sunday to 6 for Saturday, or to one day with date, an ISO date. The time list follows the picked day: below, the value is on a Saturday, so its afternoon is disabled; pick a weekday and it opens up.

Code of the example above
DateTimePickerDisabled.tsx
import { GuiDateTimePicker } from '@golemui/gui-components/react';
import type { DateRange, DisabledTimeRange } from '@golemui/gui-components';
import '@golemui/gui-components/index.css';
const christmas: DateRange[] = [{ start: '2026-12-24', end: '2026-12-26' }];
const unavailable: DisabledTimeRange[] = [
{ start: '13:00:00', end: '13:59:00' },
{ start: '16:00:00', end: '19:00:00', weekdays: [6] },
];
export function DateTimePickerDisabled() {
return (
<GuiDateTimePicker
label="Haircut"
hint="Closed over Christmas. No appointments over lunch, or after 4:00 PM on Saturdays."
minuteStep={60}
minTime="09:00:00"
maxTime="19:00:00"
value="2026-12-12T10:00:00"
disabledRanges={christmas}
disabledTimeRanges={unavailable}
/>
);
}

A typed date-time outside these is kept as the value, marked invalid for its form and reported with gui-input-error; show the message with errors, as on the Date picker. Replace the English messages with min-date-message, max-date-message, disabled-date-range-message, min-time-message, max-time-message, disabled-time-range-message, invalid-date-message and incomplete-message, or translate them for every element as in i18n.

number-of-months, the calendar formats and the month button icons work as on the Calendar. disabled, readonly and icon work as on the Date picker.

With a name, <gui-date-time-picker> submits its ISO date-time under that name, such as viewingAt=2026-11-30T10:30:00, and nothing while it is empty. These block a submit:

  • required, while the field is empty;
  • a day before min-date, after max-date or in disabled-ranges;
  • a time before min-time, after max-time or in disabled-time-ranges on that day.

An impossible date, such as February 31, doesn’t change the value, so it doesn’t block a submit by itself.

Code of the example above
DateTimePickerNativeForm.tsx
import { useState } from 'react';
import { GuiButton, GuiDateTimePicker } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function DateTimePickerNativeForm() {
const [result, setResult] = useState('');
return (
<form
id="date-time-picker-form"
onSubmit={(event) => {
event.preventDefault();
const data = Object.fromEntries(new FormData(event.currentTarget));
setResult(`Submitted ${JSON.stringify(data)}`);
}}
>
<div className="gui-grid gui-grid--row">
<div className="gui-grid__cell gui-grid__cell--span-3">
<GuiDateTimePicker
label="Viewing"
name="viewingAt"
minuteStep={30}
minTime="10:00:00"
maxTime="18:00:00"
required
/>
</div>
<div className="gui-grid__cell">
<GuiButton type="submit" label="Book" />
</div>
</div>
<p role="status">{result}</p>
</form>
);
}

How the elements submit, validate and reset is in Native forms.

<gui-date-time-picker> renders into the light DOM, so you can style every part with plain CSS. It is a <gui-date-time>, a toggle button and a panel with a <gui-date-time-calendar>, which is there only while it is open:

gui-date-time-picker
<gui-date-time-picker>
<span class="gui-label" id="appointment_label">
<span class="gui-label__text">Appointment</span>
</span>
<div class="gui-widget">
<gui-date-time id="appointment_date" class="gui-field">
<!-- The date and time parts, as in gui-date-time -->
</gui-date-time>
<button
type="button"
class="gui-date-time-picker__arrow"
aria-haspopup="dialog"
aria-label="Show calendar"
aria-expanded="true"
aria-controls="appointment_popup"
>
<span class="gui-caret" aria-hidden="true"></span>
</button>
<div class="gui-picker__panel">
<gui-date-time-calendar
role="dialog"
id="appointment_popup"
aria-labelledby="appointment_label"
class="gui-field"
>
<!-- The calendar and its time picker, as in gui-date-time-calendar -->
</gui-date-time-calendar>
</div>
</div>
<div class="gui-validator" id="appointment_errors" role="alert"></div>
</gui-date-time-picker>
ClassPart
gui-label, gui-label__textThe label and its text with the required marker.
gui-widget-hintThe hint.
gui-widgetThe box around the field, the button and the panel.
gui-date-time-picker__arrowThe button that opens and closes the popup. Its caret turns while the popup is open.
gui-picker__panelThe floating panel around the calendar.
gui-validatorThe errors container under the field. The alert role announces new errors.
gui-validator--panelA copy of the errors at the bottom of the open panel, hidden from screen readers.
gui-validator__errorOne error message.

The field’s classes are on the Date-time input page and the popup’s on the Date-time calendar page.

The field reads the tokens of the Date input, the calendar those of the Calendar and the time list those of the Time picker. The panel is the Date picker’s. Two tokens size the time grid in the popup:

TokenWhat it styles
--gui-calendar-time-grid-heightThe height of the grid of times, which scrolls.
--gui-calendar-time-button-heightThe height of each time in the grid.
styles.css
/* A taller grid of times. */
gui-date-time-picker {
--gui-calendar-time-grid-height: 280px;
}

To restyle beyond the tokens, see Overriding styles; for dark mode and the Clay theme, see Themes and dark mode.

  • Name. The label names the field’s group and the popup. Without a label the popup is named Calendar.
  • Toggle. The button at the end is named Show calendar, has aria-haspopup="dialog", reports aria-expanded and points at the popup with aria-controls. Rename it with toggle-aria-label, or translate it for every element under the key showCalendar.
  • Popup. The calendar has the dialog role. Its buttons, days and time picker are named as on the Date-time calendar; the picker takes the same *-aria-label props and time-label, and passes them on.
  • Parts. The field’s parts are named as on the Date-time input.
KeyAction
TabMoves from the field to the button, then through the popup while it is open, then out.
Enter / Space on the buttonOpens the popup and focuses today or the selected day, or closes it.
EscapeIn the open time list, closes it. Elsewhere, closes the popup and returns focus to the field.
Keys in the popupPick a day and a time, as on the Date-time calendar.
Keys in the fieldType and move between the parts, as on the Date-time input.

What every element shares, such as translating the browser messages, is in Accessibility.