Skip to content

Range Date Time Input

Range Date Time Input widgets are Input Fields that allow the user to enter multiple date and time ranges using segmented keyboard inputs. Each endpoint is a full instant (a date and a time together), and each completed range is displayed as a pill that can be removed. The date and time segments follow the locale’s order, and whether the time renders in 12-hour or 24-hour mode follows the user’s locale too.

The widget emits an array of { start, end } objects, each holding a local ISO date-time string (YYYY-MM-DDTHH:mm:ss). To capture a single date and time, use the Date Time Input instead; to capture time-only ranges, use the Range Time Input.

range-datetime-input.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.rangeDateTimeInput('dateTimeRanges', {
label: 'Date-Time Ranges',
}),
];
range-datetime-input.json
{
"$schema": "https://golemui.com/schemas/form.schema.json",
"form": [
{
"kind": "input",
"type": "rangeDateTimeInput",
"path": "dateTimeRanges",
"label": "Date-Time Ranges"
}
]
}

A range is committed once both endpoints are complete; a backward selection (end before start) reorders itself instead of erroring. Typed values clamp at their limits and ArrowUp/ArrowDown increment the focused segment. If a completed endpoint does not exist (e.g. February 30) or falls outside the allowed bounds, the widget surfaces an error instead of adding the range.

Use these props to customize your Range Date Time Input widget.

proptypedescription
endDateTimeAriaLabelstringAria label for the end date-time field group. Defaults to End date and time
hintstringA description to display below the input
hourFormat'12' | '24'Force 12- or 24-hour entry. Defaults to the user’s locale
iconstringA css class to display an icon inside the input
invalidDateMessagestring | LocalizableError shown when a typed date does not exist, e.g. February 30
maxDateTimestringLatest allowed instant (ISO date-time, inclusive)
maxDateTimeMessagestring | LocalizableError shown when a typed date-time is after maxDateTime
minDateTimestringEarliest allowed instant (ISO date-time, inclusive)
minDateTimeMessagestring | LocalizableError shown when a typed date-time is before minDateTime
minuteStepnumberGranularity of the minute segment
removePillAriaLabelstringAria label for the remove pill action. Defaults to Remove date and time
separatorstringThe separator between start and end fields. Defaults to -
startDateTimeAriaLabelstringAria label for the start date-time field group. Defaults to Start date and time

Use the property hint to add a description or validation instructions.

range-datetime-input.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.rangeDateTimeInput('dateTimeRanges', {
hint: 'Enter one or more date and time ranges',
label: 'Date-Time Ranges',
}),
];
range-datetime-input.json
{
"$schema": "https://golemui.com/schemas/form.schema.json",
"form": [
{
"kind": "input",
"type": "rangeDateTimeInput",
"path": "dateTimeRanges",
"label": "Date-Time Ranges",
"props": {
"hint": "Enter one or more date and time ranges"
}
}
]
}

Use the property icon to add an icon to the widget. The value of icon represents a set of CSS classes separated by spaces. The icon is displayed inside the input border.

range-datetime-input.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.rangeDateTimeInput('dateTimeRanges', {
icon: 'schedule',
label: 'Date-Time Ranges',
}),
];
range-datetime-input.json
{
"$schema": "https://golemui.com/schemas/form.schema.json",
"form": [
{
"kind": "input",
"type": "rangeDateTimeInput",
"path": "dateTimeRanges",
"label": "Date-Time Ranges",
"props": {
"icon": "schedule"
}
}
]
}

Use the property separator to customize the text displayed between the start and end fields. By default the separator is -.

range-datetime-input.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.rangeDateTimeInput('dateTimeRanges', {
separator: 'to',
label: 'Date-Time Ranges',
defaultValue: [
{
start: '2026-11-22T09:00:00',
end: '2026-11-22T11:00:00',
},
],
}),
];
range-datetime-input.json
{
"$schema": "https://golemui.com/schemas/form.schema.json",
"form": [
{
"kind": "input",
"type": "rangeDateTimeInput",
"path": "dateTimeRanges",
"label": "Date-Time Ranges",
"defaultValue": [
{
"start": "2026-11-22T09:00:00",
"end": "2026-11-22T11:00:00"
}
],
"props": {
"separator": "to"
}
}
]
}

By default the 12-hour/24-hour layout comes from the locale’s hour cycle. Use the property hourFormat ('12' or '24') to force one of them.

range-datetime-input.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.rangeDateTimeInput('dateTimeRanges', {
hourFormat: '24',
label: 'Date-Time Ranges',
defaultValue: [
{
start: '2026-11-22T09:00:00',
end: '2026-11-22T11:00:00',
},
],
}),
];
range-datetime-input.json
{
"$schema": "https://golemui.com/schemas/form.schema.json",
"form": [
{
"kind": "input",
"type": "rangeDateTimeInput",
"path": "dateTimeRanges",
"label": "Date-Time Ranges",
"defaultValue": [
{
"start": "2026-11-22T09:00:00",
"end": "2026-11-22T11:00:00"
}
],
"props": {
"hourFormat": "24"
}
}
]
}

Use the property minuteStep to control the ArrowUp/ArrowDown increment of the minute segment, useful for appointment-style forms where times snap to a grid (e.g. every 15 minutes). Typing a value is not restricted by the step. The default is 1.

range-datetime-input.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.rangeDateTimeInput('dateTimeRanges', {
minuteStep: 15,
label: 'Date-Time Ranges',
}),
];
range-datetime-input.json
{
"$schema": "https://golemui.com/schemas/form.schema.json",
"form": [
{
"kind": "input",
"type": "rangeDateTimeInput",
"path": "dateTimeRanges",
"label": "Date-Time Ranges",
"props": {
"minuteStep": 15
}
}
]
}

Use the properties minDateTime and maxDateTime (ISO date-times, both inclusive) to specify the boundaries for accepted instants. Each endpoint is bounded as a whole instant rather than by independent date and time axes. A completed endpoint outside the boundaries raises an input error instead of adding the range; customize (and localize) the messages with minDateTimeMessage and maxDateTimeMessage.

range-datetime-input.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.rangeDateTimeInput('dateTimeRanges', {
hint: 'Slots between Jan 1st and Dec 31st, 2026',
minDateTime: '2026-01-01T09:00:00',
maxDateTime: '2026-12-31T18:00:00',
minDateTimeMessage: 'No slots available before Jan 1st, 2026 at 9:00.',
maxDateTimeMessage: 'No slots available after Dec 31st, 2026 at 18:00.',
label: 'Available Slots',
defaultValue: [
{
start: '2026-11-22T09:00:00',
end: '2026-11-22T11:00:00',
},
{
start: '2026-11-23T14:00:00',
end: '2026-11-24T09:30:00',
},
],
}),
];
range-datetime-input.json
{
"$schema": "https://golemui.com/schemas/form.schema.json",
"form": [
{
"kind": "input",
"type": "rangeDateTimeInput",
"path": "dateTimeRanges",
"label": "Available Slots",
"defaultValue": [
{
"start": "2026-11-22T09:00:00",
"end": "2026-11-22T11:00:00"
},
{
"start": "2026-11-23T14:00:00",
"end": "2026-11-24T09:30:00"
}
],
"props": {
"hint": "Slots between Jan 1st and Dec 31st, 2026",
"minDateTime": "2026-01-01T09:00:00",
"maxDateTime": "2026-12-31T18:00:00",
"minDateTimeMessage": "No slots available before Jan 1st, 2026 at 9:00.",
"maxDateTimeMessage": "No slots available after Dec 31st, 2026 at 18:00."
}
}
]
}

Use the properties removePillAriaLabel, startDateTimeAriaLabel and endDateTimeAriaLabel to customize the accessibility labels for screen readers. These properties are localizable.

range-datetime-input.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.rangeDateTimeInput('dateTimeRanges', {
removePillAriaLabel: 'Delete range',
startDateTimeAriaLabel: 'From date and time',
endDateTimeAriaLabel: 'To date and time',
label: 'Date-Time Ranges',
}),
];
range-datetime-input.json
{
"$schema": "https://golemui.com/schemas/form.schema.json",
"form": [
{
"kind": "input",
"type": "rangeDateTimeInput",
"path": "dateTimeRanges",
"label": "Date-Time Ranges",
"props": {
"removePillAriaLabel": "Delete range",
"startDateTimeAriaLabel": "From date and time",
"endDateTimeAriaLabel": "To date and time"
}
}
]
}

Range Date Time Inputs can be styled as explained in the Styling Guide.

Following you will find a list with the CSS Variables and a quick description of what you will style.

CSS VariableDescription
--gui-intent-errorBorder and focus shadow color when invalid
--gui-border-focusBorder color when a pill or segment is focused
--gui-shadow-focusBox shadow when a pill or segment is focused
--gui-intent-primaryPill background color
--gui-color-whitePill text and remove button color
--gui-border-defaultPills wrapper border and separator border color
--gui-bg-defaultScroll shadow gradient color and dropdown background color
--gui-text-defaultActive piece (date/time segment) visual underline color
--gui-radius-mdPill and dropdown border radius
--gui-radius-fullPill count button and remove button border radius

This is the anatomy of the Range Date Time Input Widget in case you want to use your CSS styles.

range-datetime-input-anatomy.html
<gui-range-date-time class="gui-field">
<label class="gui-label" for="fieldUid" id="fieldUid_label">
Label
<div class="gui-widget-hint" id="fieldUid_hint">Hint</div>
</label>
<div class="gui-widget">
<div class="gui-widget-input gui-parts-ring gui-range-date-time-input gui-range-date-time-input--icon" role="group" aria-label="Date-time range input">
<span class="gui-widget-icon schedule" data-icon="schedule"></span>
<!-- Committed ranges render as a scrollable strip of pills -->
<gui-pills class="gui-range-date-time-input__pills">
<div class="gui-pills__strip-wrapper">
<div class="gui-pills__strip" role="list" tabindex="-1">
<span class="gui-sentinel gui-sentinel__start"></span>
<div class="gui-pills__pill gui-pills__pill--clickable" role="listitem" data-key="2026-11-22T09:00:00|2026-11-22T11:00:00" tabindex="0" aria-label="Remove date-time: 11/22/2026, 9:00 AM - 11/22/2026, 11:00 AM">
<span class="gui-pills__pill-text">11/22/2026, 9:00 AM - 11/22/2026, 11:00 AM</span>
<button type="button" class="gui-pills__pill-remove" tabindex="-1" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 256 256" fill="currentColor" aria-hidden="true"><path d="M165.66,101.66,139.31,128l26.35,26.34a8,8,0,0,1-11.32,11.32L128,139.31l-26.34,26.35a8,8,0,0,1-11.32-11.32L116.69,128,90.34,101.66a8,8,0,0,1,11.32-11.32L128,116.69l26.34-26.35a8,8,0,0,1,11.32,11.32Z"></path></svg>
</button>
</div>
<span class="gui-sentinel gui-sentinel__end"></span>
</div>
</div>
<!-- Count bubble + dropdown shown when the host shrinks below the compact threshold -->
<div class="gui-pills__compact">
<button type="button" class="gui-pills__count" aria-label="1 date-time ranges" aria-expanded="false">1</button>
</div>
</gui-pills>
<div class="gui-range-date-time-input__inputs">
<!-- Start endpoint: date + time segments, locale order (en-US shown) -->
<div class="gui-parts gui-range-date-time-input__field" role="group" aria-label="Start date-time">
<div class="gui-range-date-time-input__touch-target">
<input type="text" inputmode="numeric" class="gui-range-date-time-input__part" data-type="month" data-group="start" maxlength="2" placeholder="mm" tabindex="0">
<div class="gui-range-date-time-input__visual-underline"></div>
</div>
<span class="gui-range-date-time-input__separator">/</span>
<div class="gui-range-date-time-input__touch-target">
<input type="text" inputmode="numeric" class="gui-range-date-time-input__part" data-type="day" data-group="start" maxlength="2" placeholder="dd" tabindex="-1">
<div class="gui-range-date-time-input__visual-underline"></div>
</div>
<span class="gui-range-date-time-input__separator">/</span>
<div class="gui-range-date-time-input__touch-target">
<input type="text" inputmode="numeric" class="gui-range-date-time-input__part gui-range-date-time-input__year" data-type="year" data-group="start" maxlength="4" placeholder="yyyy" tabindex="-1">
<div class="gui-range-date-time-input__visual-underline"></div>
</div>
<span class="gui-range-date-time-input__separator">, </span>
<div class="gui-range-date-time-input__touch-target">
<input type="text" inputmode="numeric" class="gui-range-date-time-input__part" data-type="hour" data-group="start" maxlength="2" placeholder="hh" tabindex="-1">
<div class="gui-range-date-time-input__visual-underline"></div>
</div>
<span class="gui-range-date-time-input__separator">:</span>
<div class="gui-range-date-time-input__touch-target">
<input type="text" inputmode="numeric" class="gui-range-date-time-input__part" data-type="minute" data-group="start" maxlength="2" placeholder="mm" tabindex="-1">
<div class="gui-range-date-time-input__visual-underline"></div>
</div>
<!-- Only rendered in 12-hour mode, at the locale's position -->
<div class="gui-range-date-time-input__touch-target">
<button type="button" class="gui-range-date-time-input__part gui-range-date-time-input__dayperiod" data-type="dayPeriod" data-group="start" tabindex="-1" aria-label="AM/PM">AM</button>
<div class="gui-range-date-time-input__visual-underline"></div>
</div>
</div>
<span class="gui-range-date-time-input__separator">-</span>
<!-- End endpoint: same segment layout -->
<div class="gui-parts gui-range-date-time-input__field" role="group" aria-label="End date-time">
<div class="gui-range-date-time-input__touch-target">
<input type="text" inputmode="numeric" class="gui-range-date-time-input__part" data-type="month" data-group="end" maxlength="2" placeholder="mm" tabindex="-1">
<div class="gui-range-date-time-input__visual-underline"></div>
</div>
<span class="gui-range-date-time-input__separator">/</span>
<div class="gui-range-date-time-input__touch-target">
<input type="text" inputmode="numeric" class="gui-range-date-time-input__part" data-type="day" data-group="end" maxlength="2" placeholder="dd" tabindex="-1">
<div class="gui-range-date-time-input__visual-underline"></div>
</div>
<span class="gui-range-date-time-input__separator">/</span>
<div class="gui-range-date-time-input__touch-target">
<input type="text" inputmode="numeric" class="gui-range-date-time-input__part gui-range-date-time-input__year" data-type="year" data-group="end" maxlength="4" placeholder="yyyy" tabindex="-1">
<div class="gui-range-date-time-input__visual-underline"></div>
</div>
<span class="gui-range-date-time-input__separator">, </span>
<div class="gui-range-date-time-input__touch-target">
<input type="text" inputmode="numeric" class="gui-range-date-time-input__part" data-type="hour" data-group="end" maxlength="2" placeholder="hh" tabindex="-1">
<div class="gui-range-date-time-input__visual-underline"></div>
</div>
<span class="gui-range-date-time-input__separator">:</span>
<div class="gui-range-date-time-input__touch-target">
<input type="text" inputmode="numeric" class="gui-range-date-time-input__part" data-type="minute" data-group="end" maxlength="2" placeholder="mm" tabindex="-1">
<div class="gui-range-date-time-input__visual-underline"></div>
</div>
<div class="gui-range-date-time-input__touch-target">
<button type="button" class="gui-range-date-time-input__part gui-range-date-time-input__dayperiod" data-type="dayPeriod" data-group="end" tabindex="-1" aria-label="AM/PM">AM</button>
<div class="gui-range-date-time-input__visual-underline"></div>
</div>
</div>
</div>
</div>
</div>
</gui-range-date-time>