DateTimeRangePicker

A span with a time at each end (date-time range): a planned downtime, a release freeze, the minutes an objective was breached. The footer shows the duration while it is set.

import { DateTimeRangePicker } from "@umriss-ui/core";

It starts on All day: two clicks set the days, from 00:00 to the end of the last day. Untick All day to type the times. It reports only on Apply. The value contract of the pickers stands on DatePicker.

A clock change is named for each end separately.

Examples

A downtime window

Click two days, untick All day, type both times, then Apply; the footer counts the duration as you go.

import { useState } from "react";
import { DateTimeRangePicker, FormField, Stack, Text, useFormats } from "@umriss-ui/core";
import type { DateRange } from "@umriss-ui/core";

export default function ADowntimeWindow() {
  const formats = useFormats();
  const [downtime, setDowntime] = useState<DateRange | null>(null);

  return (
    <Stack gap={3} style={{ maxWidth: 360 }}>
      <FormField label="Planned downtime, Billing">
        <DateTimeRangePicker value={downtime} onChange={setDowntime} clearable />
      </FormField>
      <Text size="xs" tone="muted" mono>
        {downtime === null
          ? "value: null"
          : `${formats.dateTime(downtime.from, false)} to ${formats.dateTime(downtime.to, false)}`}
      </Text>
    </Stack>
  );
}

With seconds

Set withSeconds where the span is measured to the second; presets={[]} drops the column when no span repeats.

import { useState } from "react";
import { DateTimeRangePicker, FormField } from "@umriss-ui/core";
import type { DateRange } from "@umriss-ui/core";

export default function WithSeconds() {
  const [breach, setBreach] = useState<DateRange | null>({
    from: new Date(2026, 2, 17, 9, 40, 12),
    to: new Date(2026, 2, 17, 10, 9, 48),
  });

  return (
    <FormField label="Objective breached, Checkout" style={{ maxWidth: 420 }}>
      <DateTimeRangePicker value={breach} onChange={setBreach} withSeconds presets={[]} />
    </FormField>
  );
}

States

size="sm" suits dense forms; errors come from the FormField, and disabled locks field and panel.

import { useState } from "react";
import { DateTimeRangePicker, FormField, Grid } from "@umriss-ui/core";
import type { DateRange } from "@umriss-ui/core";

export default function States() {
  const [freeze, setFreeze] = useState<DateRange | null>(null);
  const [downtime, setDowntime] = useState<DateRange | null>({
    from: new Date(2026, 2, 21, 2, 0),
    to: new Date(2026, 2, 21, 6, 0),
  });

  return (
    <Grid minItemWidth="260px" gap={4}>
      <FormField label="Release freeze" hint="Small size.">
        <DateTimeRangePicker size="sm" value={freeze} onChange={setFreeze} clearable />
      </FormField>
      <FormField label="Planned downtime, Search" error="Overlaps the downtime of Checkout, 03:00 to 04:00.">
        <DateTimeRangePicker value={downtime} onChange={setDowntime} clearable />
      </FormField>
      <FormField label="Maintenance window, Payments" hint="Empty, with its own placeholder.">
        <DateTimeRangePicker value={null} onChange={() => {}} placeholder="Not scheduled yet" />
      </FormField>
      <FormField label="Downtime, last quarter" hint="Closed.">
        <DateTimeRangePicker
          value={{ from: new Date(2025, 11, 6, 1, 0), to: new Date(2025, 11, 6, 3, 30) }}
          onChange={() => {}}
          disabled
        />
      </FormField>
    </Grid>
  );
}

With an error

A FormField with an error marks the field invalid by itself. Without one, set invalid and tie your message to the field with aria-describedby.

import { useId, useState } from "react";
import { DateTimeRangePicker, Stack, Text } from "@umriss-ui/core";
import type { DateRange } from "@umriss-ui/core";

/* Checkout is down from 03:00 to 04:00 that night; Search depends on it. */
const CHECKOUT = { from: new Date(2026, 2, 21, 3, 0), to: new Date(2026, 2, 21, 4, 0) };

export default function WithAnError() {
  const [downtime, setDowntime] = useState<DateRange | null>({
    from: new Date(2026, 2, 21, 2, 0),
    to: new Date(2026, 2, 21, 6, 0),
  });
  const messageId = useId();
  const overlaps = downtime !== null && downtime.from < CHECKOUT.to && downtime.to > CHECKOUT.from;
  const error = overlaps ? "Overlaps the downtime of Checkout, 03:00 to 04:00." : undefined;

  return (
    <Stack gap={1} style={{ maxWidth: 420 }}>
      <DateTimeRangePicker
        aria-label="Planned downtime, Search"
        value={downtime}
        onChange={setDowntime}
        invalid={error !== undefined}
        aria-describedby={error ? messageId : undefined}
      />
      {error && (
        <Text id={messageId} size="xs" style={{ color: "var(--u-color-danger-text)" }}>
          {error}
        </Text>
      )}
    </Stack>
  );
}

When to use something else

Keyboard

Key Action
Arrow keys In the calendar, move a day or a week.
Enter or Space Sets the start day, then the end day.
Arrow Up / Arrow Down In a time field, steps its value.
Enter In a time field, applies the span.
Escape Closes the panel without a change.

API

DateTimeRangePickerProps

NameTypeDefaultDescription
valuerequiredDateRange | null—The time span with times at both ends; null means none.

Shown in: Configurator, A downtime window, With seconds and 3 more

withSecondsbooleanfalseShow the seconds fields.

Shown in: Configurator, With seconds

placeholderstring—What stands in the empty field.

Shown in: States

disabledbooleanfalseLocks the field and the panel.

Shown in: Configurator, States

invalidboolean—Marks the field as invalid. FormField sets it itself as soon as it carries an error - by hand only necessary without FormField.

Shown in: Configurator, With an error

size"sm" | "md"the size of a ControlSizeProvider around it, else "md"sm for a toolbar and dense forms, md otherwise.

Shown in: Configurator, States

charsnumber—The width in characters - room for the value; the field adds its own padding, glyph and cross. Given, the field is that wide wherever it stands, and never wider than its place. Without it the field fills its place, and where the place asks - in a toolbar or a row - it is as wide as its longest value in the formats in use, so a date never truncates.

Shown in: Configurator

clearablebooleanfalseShows a cross where a value is set, which fades in on hover/focus and clears the value.

Shown in: Configurator, A downtime window, States

presetsRangePreset[]DEFAULT_PRESETS, in the wording's labelsThe quick-select column in the panel; presets apply for the whole day and close immediately. An empty array hides the column.

Shown in: With seconds

refRef<HTMLSpanElement>—Receives the component's root element. from RefProp

Shown in: A row that holds still (Sizes), An anchored surface (Popover), Take the width of the trigger (Popover) and 2 more

Events

NameTypeDefaultDescription
onChangerequired(range: DateRange | null) => void—Runs only on "Apply" and on clearing (null), never between the two clicks. If the end lies before the start, both swap on applying - there is no error state.

Shown in: Configurator, A downtime window, With seconds and 3 more

Also takes every attribute of <span> – without defaultValue, onChange.

Types on this page

DateRange

A span between two dates, both ends included - the value of DateRangePicker and DateTimeRangePicker.

NameTypeDefaultDescription
fromrequiredDate—

Shown in: A downtime window, With seconds, States and 6 more

torequiredDate—

Shown in: A downtime window, With seconds, States and 6 more

RangePreset

A shortcut in a range picker's panel: its label, and a function that computes the range when it is chosen - so that "Last 7 days" is counted from the moment of the click.

NameTypeDefaultDescription
labelrequiredstring—

Shown in: Presets (DateRangePicker)

rangerequired() => DateRange—

Shown in: Presets (DateRangePicker)

Known limits

What umriss deliberately does not build, and why: ADR-0032.

Every page of @umriss-ui/core

Getting started

Customising

Layout

Typography

Actions

Forms

Choosing

Dates and times

Feedback

Overlays

Navigation

Data display

API index