DatePicker

One day, picked in a calendar (date field): a delivery date, a due date. Weeks begin on Monday.

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

The value contract of the four pickers stands here. DatePicker hands out a Date at local midnight on every path and reports as soon as the day is chosen. DateTimePicker hands out an instant and reports only on Apply, Now or Clear. DateRangePicker gives both ends at local midnight, both inclusive. DateTimeRangePicker sets an all-day end to the end of the day, not to its midnight.

null means no date, never 1 January 1970. A range never comes out backwards: ends picked in reverse are swapped, so there is no error state to handle. A time that falls into a clock change is named in the panel, never guessed: a skipped hour moves forward, and a doubled hour asks which of the two.

Examples

Pick a day

The value is a Date at local midnight or null; onChange runs as soon as a day is chosen.

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

export default function PickADay() {
  const formats = useFormats();
  const [delivery, setDelivery] = useState<Date | null>(null);

  return (
    <Stack gap={3} style={{ maxWidth: 320 }}>
      <FormField label="Delivery date">
        <DatePicker value={delivery} onChange={setDelivery} clearable />
      </FormField>
      <Text size="xs" tone="muted" mono>
        value: {delivery === null ? "null" : formats.dateTime(delivery, true)}
      </Text>
    </Stack>
  );
}

States

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

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

export default function States() {
  const [pickUp, setPickUp] = useState<Date | null>(new Date(2026, 2, 18));
  const [delivery, setDelivery] = useState<Date | null>(new Date(2026, 2, 16));

  return (
    <Grid minItemWidth="220px" gap={4}>
      <FormField label="Pick-up" hint="Small size.">
        <DatePicker size="sm" value={pickUp} onChange={setPickUp} clearable />
      </FormField>
      <FormField label="Delivery" error="The delivery date cannot lie before today.">
        <DatePicker value={delivery} onChange={setDelivery} clearable />
      </FormField>
      <FormField label="Delivered on" hint="Set by the driver's scan.">
        <DatePicker value={null} onChange={() => {}} disabled />
      </FormField>
      <FormField label="Return by" hint="Empty, with its own placeholder.">
        <DatePicker value={null} onChange={() => {}} placeholder="No return planned" />
      </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 { DatePicker, Stack, Text } from "@umriss-ui/core";

export default function WithAnError() {
  const [delivery, setDelivery] = useState<Date | null>(new Date(2026, 2, 16));
  const messageId = useId();
  const today = new Date();
  today.setHours(0, 0, 0, 0);
  const error = delivery !== null && delivery < today ? "The delivery date cannot lie before today." : undefined;

  return (
    <Stack gap={1} style={{ maxWidth: 280 }}>
      <DatePicker
        aria-label="Delivery"
        value={delivery}
        onChange={setDelivery}
        invalid={error !== undefined}
        aria-describedby={error ? messageId : undefined}
      />
      {error && (
        <Text id={messageId} size="xs" style={{ color: "var(--u-color-danger-text)" }}>
          {error}
        </Text>
      )}
    </Stack>
  );
}

Check a due date

Compare two pickers' values as plain dates, since both lie at local midnight, and put the result in the FormField.

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

const DAY = 24 * 60 * 60 * 1000;

export default function CheckADueDate() {
  const [issued, setIssued] = useState<Date | null>(new Date(2026, 2, 2));
  const [due, setDue] = useState<Date | null>(new Date(2026, 1, 27));

  const term = issued && due ? Math.round((due.getTime() - issued.getTime()) / DAY) : null;
  const error = term !== null && term < 0 ? "The due date lies before the invoice date." : undefined;
  const hint = term !== null && term >= 0 ? `Payment term: ${term} days.` : undefined;

  return (
    <Grid minItemWidth="220px" gap={4} style={{ maxWidth: 520 }}>
      <FormField label="Invoice date">
        <DatePicker value={issued} onChange={setIssued} />
      </FormField>
      <FormField label="Due" hint={hint} error={error}>
        <DatePicker value={due} onChange={setDue} />
      </FormField>
    </Grid>
  );
}

When to use something else

Keyboard

Key Action
Enter or Space On the field, opens the calendar; on a day, chooses it.
Arrow keys Move a day left or right, a week up or down.
Escape Closes the calendar and returns to the field.

API

DatePickerProps

NameTypeDefaultDescription
valuerequiredDate | null—The day, as a Date on local midnight; null means none.

Shown in: Configurator, Pick a day, States and 8 more

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, Pick a day, States and 4 more

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(date: Date | null) => void—Runs as soon as a day is settled - on a click in the grid, on "Today" and on clearing (null). Every one of these ways hands out local midnight; there is none that delivers a time along with it.

Shown in: Configurator, Pick a day, States and 8 more

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

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