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.
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>
);
}
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>
);
}
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>
);
}
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>
);
}
| 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. |
DatePickerProps| Name | Type | Default | Description |
|---|---|---|---|
valuerequired | Date | null | — | The day, as a Date on local midnight; null means none.Shown in: Configurator, Pick a day, States and 8 more |
placeholder | string | — | What stands in the empty field. Shown in: States |
disabled | boolean | false | Locks the field and the panel. Shown in: Configurator, States |
invalid | boolean | — | 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 |
chars | number | — | 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 |
clearable | boolean | false | Shows 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 |
ref | Ref<HTMLSpanElement> | — | Receives the component's root element. from RefPropShown in: A row that holds still (Sizes), An anchored surface (Popover), Take the width of the trigger (Popover) and 2 more |
| Name | Type | Default | Description |
|---|---|---|---|
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.
What umriss deliberately does not build, and why: ADR-0032.