Skip to content
Components, switch product

Date-time calendar

<gui-date-time-calendar> is a Calendar with a Time picker on top, always visible, to pick a date and a time. Its value is a local ISO date-time, such as 2026-10-05T18:45:00. For a field that opens it in a popup, use the Date-time picker.

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

The element’s entry point is @golemui/gui-components/date-time-calendar, which registers <gui-date-time-calendar> and the <gui-time-picker> inside it. 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.

PickupAtField.tsx
import { useState } from 'react';
import { GuiDateTimeCalendar } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function PickupAtField() {
const [pickupAt, setPickupAt] = useState('');
return (
<GuiDateTimeCalendar
label="Pickup"
name="pickupAt"
value={pickupAt}
onGuiChange={(event) => setPickupAt(event.detail.value ?? '')}
/>
);
}

Properties

Property Type Default Description
showHint attribute show-hint boolean true Whether the element renders its hint. Elements that embed it turn it off and show the hint themselves: aria-describedby still points at the hint by its id.
localeId attribute locale-id string – BCP 47 locale for formatting and parsing, such as en-US or es.
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' 'numeric' How day numbers are written.
weekdayFormat attribute weekday-format 'short' | 'long' | 'narrow' 'narrow' How weekday names are written in the header.
monthFormat attribute month-format 'numeric' | '2-digit' | 'long' | 'short' | 'narrow' 'long' 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 1 Number of months shown side by side.
value string – The date and time, as an ISO date-time (YYYY-MM-DDTHH:mm:ss).
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.
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.
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. An empty value keeps the default.
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 picked a time for the chosen day. detail.value is the date-time.
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.
gui-parts-change The typed parts changed before they form a complete value, for a host that mirrors them.

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, and the calendar shows its month. The value needs a day and a time. The time picker’s list opens in place of the days when its field gets focus, or from its button, and closes when the user picks a time.

  • Picking a time, with a day picked, fires gui-input and then gui-change.
  • Picking another day, with a time picked, keeps the time and fires gui-input only.
  • A day without a time, or a time without a day, sets no value yet.

Value: 2026-11-18T11:00:00

Code of the example above
DateTimeCalendarValue.tsx
import { useState } from 'react';
import { GuiDateTimeCalendar } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function DateTimeCalendarValue() {
const [value, setValue] = useState<string | undefined>('2026-11-18T11:00:00');
const [lastEvent, setLastEvent] = useState<'gui-input' | 'gui-change'>();
return (
<>
<GuiDateTimeCalendar
id="date-time-calendar-tour"
label="Museum tour"
value={value}
onGuiInput={(event) => {
setValue(event.detail.value);
setLastEvent('gui-input');
}}
onGuiChange={(event) => {
setValue(event.detail.value);
setLastEvent('gui-change');
}}
/>
<p role="status">
{lastEvent ? `${lastEvent}: ${value}` : `Value: ${value}`}
</p>
</>
);
}

The time picker 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 is its label: Time by default. allow-custom-time lets the user type any time in it, as in the Time picker. locale-id and hour-format set the clock, and locale-id the names of the calendar, as on the Calendar.

Code of the example above
DateTimeCalendarTimes.tsx
import { GuiDateTimeCalendar } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function DateTimeCalendarTimes() {
return (
<GuiDateTimeCalendar
label="Tasting"
timeLabel="Seating"
localeId="en-GB"
minuteStep={15}
minTime="18:00:00"
maxTime="21:00:00"
value="2026-12-14T19:30:00"
/>
);
}

min-date, max-date and disabled-ranges disable days, as on the Calendar. 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: pick a Friday below, or December 16.

Code of the example above
DateTimeCalendarDisabled.tsx
import { GuiDateTimeCalendar } from '@golemui/gui-components/react';
import type { DisabledTimeRange } from '@golemui/gui-components';
import '@golemui/gui-components/index.css';
const afternoonsOff: DisabledTimeRange[] = [
{ start: '12:00:00', end: '17:00:00', weekdays: [5] },
{ start: '12:00:00', end: '17:00:00', date: '2026-12-16' },
];
export function DateTimeCalendarDisabled() {
return (
<GuiDateTimeCalendar
label="Workshop slot"
hint="Mornings only on Fridays and on December 16."
minuteStep={60}
minTime="09:00:00"
maxTime="17:00:00"
value="2026-12-14T10:00:00"
disabledTimeRanges={afternoonsOff}
/>
);
}

Replace the English messages with min-time-message, max-time-message, disabled-time-range-message, incomplete-message and no-available-times-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.

With a name, <gui-date-time-calendar> submits its ISO date-time under that name, such as deliverAt=2026-11-18T14:00:00, and nothing until both a day and a time are picked. required blocks the submit until then. A disabled calendar can’t be used and isn’t submitted; a readonly one shows its value and is submitted, but can’t be changed.

Code of the example above
DateTimeCalendarNativeForm.tsx
import { useState } from 'react';
import { GuiButton, GuiDateTimeCalendar } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function DateTimeCalendarNativeForm() {
const [result, setResult] = useState('');
return (
<form
id="date-time-calendar-form"
onSubmit={(event) => {
event.preventDefault();
const data = Object.fromEntries(new FormData(event.currentTarget));
setResult(`Submitted ${JSON.stringify(data)}`);
}}
>
<GuiDateTimeCalendar
label="Delivery"
name="deliverAt"
minuteStep={60}
minTime="08:00:00"
maxTime="20:00:00"
required
/>
<p>
<GuiButton type="submit" label="Confirm delivery" />
</p>
<p role="status">{result}</p>
</form>
);
}

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

<gui-date-time-calendar> renders into the light DOM, so you can style every part with plain CSS. It is the Calendar’s markup, with a row for a Time picker under the first month’s header. Its list is a grid of four columns, and while it is open it takes the place of the days. Here in en-US:

gui-date-time-calendar
<gui-date-time-calendar class="gui-field">
<label class="gui-label" for="pickup" id="pickup_calendar_label">
<span class="gui-label__text">Pickup</span>
</label>
<div class="gui-widget">
<div
class="gui-calendar-input"
role="group"
id="pickup_calendar"
aria-labelledby="pickup_calendar_label"
>
<div class="gui-visually-hidden" aria-live="polite">October 2026</div>
<div class="gui-calendar__container">
<!-- The previous-month button -->
<div class="gui-calendar__months-grid">
<div class="gui-calendar__panel">
<header class="gui-calendar__header">
<!-- The month name and the year button -->
</header>
<div class="gui-calendar__time-input-row">
<gui-time-picker class="gui-time-picker gui-field">
<span class="gui-label" id="pickup-time_label">
<span class="gui-label__text">Time</span>
</span>
<div class="gui-widget">
<!-- The time field and its button, as in gui-time-picker -->
<div class="gui-picker__panel" hidden>
<gui-time-list
role="dialog"
id="pickup-time_popup"
aria-labelledby="pickup-time_label"
hidden
>
<div
class="gui-time-list__viewport gui-time-list__viewport--grid"
role="grid"
aria-label="Time"
>
<div role="row" class="gui-time-list__row">
<button
type="button"
role="gridcell"
class="gui-time-list__option"
tabindex="-1"
data-value="00:00:00"
aria-selected="false"
aria-disabled="false"
>
12:00 AM
</button>
<!-- Three more times -->
</div>
<!-- The other rows -->
</div>
</gui-time-list>
</div>
</div>
</gui-time-picker>
</div>
<div
class="gui-calendar__days-grid"
role="grid"
aria-label="October 2026"
>
<!-- The weekdays and the days, as in gui-calendar -->
</div>
</div>
</div>
<!-- The next-month button -->
</div>
</div>
</div>
</gui-date-time-calendar>
ClassPart
gui-calendar__time-input-rowThe row of the time picker, under the first month’s header.
gui-time-pickerThe time picker in that row.

The other classes are those of the Calendar and the Time picker.

The calendar reads the tokens of the Calendar, and the time picker those of the Time picker. Two more size its grid of times:

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, with roomier times. */
gui-date-time-calendar {
--gui-calendar-time-grid-height: 280px;
--gui-calendar-time-button-height: 40px;
}

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 calendar’s group. The days, the month buttons, the year button and the year grid are named as on the Calendar, with the same *-aria-label props.
  • Time. The time picker is labelled Time, which names its field and its list. Change it with time-label, or translate it for every element under the key time. Its button and parts are named as on the Time picker.
  • List. The times are a grid of four columns, each time a grid cell with aria-selected.
KeyAction
TabMoves between the month buttons, the year button, the time field, its button and the days grid.
Keys in the days grid and the year gridAs on the Calendar.
Keys in the time field and the listAs on the Time picker.
Escape in the listCloses the list, shows the days again and returns focus to the time field.

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