Skip to content
Components, switch product

Date-time input

<gui-date-time> is a date and time field typed part by part: the day, month and year of a Date input, then the hour, minute and AM/PM of a Time input, in one field. Its value is a local ISO date-time, such as 2026-10-05T18:45:00. To pick it from a calendar and a list of times too, use the Date-time picker.

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

The element’s entry point is @golemui/gui-components/date-time-input, which registers <gui-date-time> and nothing else. 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.

DepartureField.tsx
import { useState } from 'react';
import { GuiDateTime } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function DepartureField() {
const [departure, setDeparture] = useState('');
return (
<GuiDateTime
label="Departure"
name="departure"
value={departure}
onGuiChange={(event) => setDeparture(event.detail.value ?? '')}
/>
);
}

Properties

Property Type Default Description
localeId attribute locale-id string – BCP 47 locale for formatting and parsing, such as en-US or es.
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.
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.
icon string '' Icon class name shown inside the control, for example from an icon font.
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.
hourFormat attribute hour-format HourFormat – 12- or 24-hour clock. Defaults to the locale's.
minuteStep attribute minute-step number 1 Minutes between the times offered in the list.
invalidDateMessage attribute invalid-date-message string – Error for a complete but impossible date, such as February 31.
value string – The date and time, as an ISO date-time (YYYY-MM-DDTHH:mm:ss).
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).
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).
minDateMessage attribute min-date-message string – Error for a date before minDate.
maxDateMessage attribute max-date-message string – Error for a date after maxDate.
minTimeMessage attribute min-time-message string – Error for a time before minTime.
maxTimeMessage attribute max-time-message string – Error for a time after maxTime.
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-focus One of the parts of the field received focus.
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.

value takes a local ISO date-time, YYYY-MM-DDTHH:mm:ss, with no time zone, on a 24-hour clock whatever the field shows. A typed value always has 00 seconds. The element fires gui-input and then gui-change as soon as every part is filled, with the date-time in event.detail.value. When the user clears a part, the value becomes empty and both events fire with null.

Value: 2026-11-30T09:30:00

Code of the example above
DateTimeValue.tsx
import { useState } from 'react';
import { GuiDateTime } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function DateTimeValue() {
const [value, setValue] = useState<string | undefined>('2026-11-30T09:30:00');
return (
<>
<GuiDateTime
id="date-time-launch"
label="Launch"
value={value}
onGuiChange={(event) => setValue(event.detail.value)}
/>
<p role="status">Value: {value}</p>
</>
);
}

locale-id takes a BCP 47 locale, such as en-US or en-GB. It sets the order of the date parts, the separators, the clock and the AM/PM labels. hour-format, 12 or 24, sets the clock whatever the locale. Without either, the field follows English: month, day, year, and a 12-hour clock.

Code of the example above
DateTimeLocale.tsx
import { GuiDateTime } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function DateTimeLocale() {
return (
<div className="gui-grid gui-grid--row">
<div className="gui-grid__cell">
<GuiDateTime
label="United States"
localeId="en-US"
value="2026-07-04T18:45:00"
/>
</div>
<div className="gui-grid__cell">
<GuiDateTime
label="United Kingdom"
localeId="en-GB"
value="2026-07-04T18:45:00"
/>
</div>
</div>
);
}

minute-step sets how many minutes the arrow keys add to the minute part, as on the Time input.

min-date and max-date bound the day, and min-time and max-time the time on every day. All four are inclusive, the dates as ISO dates and the times as ISO times. A value outside them is still entered: the element keeps it, marks it as invalid for its form and fires gui-input-error with the reason. Show it with errors, as below. Type a time after 6:00 PM to see it.

Code of the example above
DateTimeBounds.tsx
import { useState } from 'react';
import { GuiDateTime } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function DateTimeBounds() {
const [errors, setErrors] = useState<string[]>([]);
return (
<GuiDateTime
id="date-time-viewing"
label="Viewing"
hint="Any day in 2027, between 10:00 AM and 6:00 PM."
minDate="2027-01-01"
maxDate="2027-12-31"
minTime="10:00:00"
maxTime="18:00:00"
errors={errors}
// The element reports what it rejects; show it with errors.
onGuiInputError={(event) => setErrors([event.detail.message])}
onGuiInput={() => setErrors([])}
/>
);
}

gui-input-error also fires for an impossible date, such as February 31, and when focus leaves a partly filled field. Replace the English messages with min-date-message, max-date-message, min-time-message, max-time-message, invalid-date-message and incomplete-message, or translate them for every element as in i18n.

disabled, readonly and icon work as on the Date input.

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

  • required, while the field is empty;
  • a day before min-date or after max-date;
  • a time before min-time or after max-time;
  • an impossible date, such as February 31.

Submit the empty field, then try a time after 20:00.

Code of the example above
DateTimeNativeForm.tsx
import { useState } from 'react';
import { GuiButton, GuiDateTime } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function DateTimeNativeForm() {
const [result, setResult] = useState('');
return (
<form
id="date-time-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">
<GuiDateTime
label="Starts"
name="startsAt"
localeId="en-GB"
minTime="08:00:00"
maxTime="20:00:00"
required
/>
</div>
<div className="gui-grid__cell">
<GuiButton type="submit" label="Schedule" />
</div>
</div>
<p role="status">{result}</p>
</form>
);
}

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

<gui-date-time> renders into the light DOM, so you can style every part with plain CSS. These are the parts and their public classes, here with en-US parts:

gui-date-time
<gui-date-time class="gui-field">
<span class="gui-label" id="departure_label">
<span class="gui-label__text">Departure</span>
</span>
<div class="gui-widget">
<div
role="group"
id="departure"
class="gui-widget-input gui-parts gui-parts-ring gui-date-time-input"
aria-labelledby="departure_label"
>
<div class="gui-parts__touch-target gui-date-time-input__touch-target">
<input
type="text"
inputmode="numeric"
role="spinbutton"
class="gui-parts__part gui-date-time-input__part"
data-type="month"
maxlength="2"
placeholder="mm"
tabindex="0"
aria-label="Month"
aria-valuemin="1"
aria-valuemax="12"
aria-valuenow="10"
/>
<div
class="gui-parts__visual-underline gui-date-time-input__visual-underline"
></div>
</div>
<span class="gui-date-time-input__separator">/</span>
<!-- The day and the year (with gui-parts__year gui-date-time-input__year) -->
<span class="gui-date-time-input__separator">, </span>
<div
dir="ltr"
class="gui-parts__time-cluster gui-date-time-input__time-cluster"
>
<!-- The hour, a ":" separator and the minute -->
</div>
<span class="gui-date-time-input__separator"> </span>
<div class="gui-parts__touch-target gui-date-time-input__touch-target">
<button
type="button"
class="gui-parts__part gui-parts__dayperiod gui-date-time-input__part gui-date-time-input__dayperiod"
data-type="dayPeriod"
tabindex="-1"
aria-label="AM/PM"
>
AM
</button>
<div
class="gui-parts__visual-underline gui-date-time-input__visual-underline"
></div>
</div>
</div>
</div>
</gui-date-time>

The classes are those of the Date input and the Time input, with the block gui-date-time-input: gui-date-time-input__part, __touch-target, __year, __separator, __time-cluster, __dayperiod and __visual-underline, next to the shared gui-parts classes. The ids derive from uid, or from a generated id when you don’t set one.

The field reads the same design tokens as the Date input. Set them on :root to change every field, or on any element to change the fields inside it.

styles.css
/* Taller date-time fields in one form only. */
.schedule gui-date-time {
--gui-widget-size: 48px;
}

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

  • Name. The parts sit in a group that the label names. Without a label the group has no name, and development builds log a warning.
  • Parts. The parts are spin buttons named Day, Month, Year, Hour and Minute, and the AM/PM toggle a button named AM/PM. Rename them with day-aria-label, month-aria-label, year-aria-label, hour-aria-label, minute-aria-label and day-period-aria-label, or translate them for every element under the keys day, month, year, hour, minute and dayPeriod.
  • One tab stop. Only the first part is in the tab order. The arrow keys move between the parts.
  • Description and errors. The hint describes the group, and errors mark it with aria-invalid and aria-errormessage, as on the Date input.

The keys are those of the Date input and the Time input:

KeyAction
TabMoves focus to the first part, and out of the field.
0 to 9Type the part. A full part moves focus to the next part that is still empty.
↑ / ↓Add or subtract one, or minute-step minutes. On the AM/PM toggle, switch it.
SpaceOn the AM/PM toggle, switches it.
← / →Move to the part on the left or right, in the order you see them.
Backspace / DeleteDelete digits. Leaving a part empty clears the value.

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