Date Picker

Field capsule that opens a calendar on overlay glass. The trigger is the same raised capsule as Input; the day grid draws chips only and takes its material from the popover.

Basic

const [date, setDate] = React.useState<Date | null>(null);
<DatePicker label="Start date" value={date} onValueChange={setDate} />

With a value

Once a date is set, a clear control appears inside the capsule as a trailing control. The capsule is the container and the trigger is a transparent row within it — a button can’t be nested in a button, and the clear must not open the picker.

<DatePicker label="Renewal date" value={value} onValueChange={setValue} />
<DatePicker … clearable={false} />
<DatePicker … disabled />

Accepts wire dates

A YYYY-MM-DD string is read as a local calendar day, not UTC midnight — otherwise the date renders one day early for every user west of Greenwich.

<DatePicker label="From API" value="2026-07-04" onValueChange={onChange} />

Date utilities

Both pickers speak YYYY-MM-DD on the wire, and the conversion is exported so apps don’t reimplement it. Use these anywhere you turn an API date into a Date or back — not just with the pickers.

import { parseLocalDate, formatWireDate } from '@tra/ui';
// API string → Date (local calendar day)
const start = parseLocalDate('2026-07-04'); // Jul 4 2026, local midnight
parseLocalDate(someDate); // passes Dates through
parseLocalDate(null); // undefined
// Date → API string (local calendar day)
formatWireDate(start); // '2026-07-04'
formatWireDate(null); // ''
// Invalid days are rejected, not rolled over
parseLocalDate('2026-02-31'); // undefined

Why not `new Date(str)`

new Date('2026-07-04') parses as UTC midnight, so it renders as July 3rd for anyone west of Greenwich — which is every TRA user. toISOString().slice(0, 10) has the same bug going out, turning a late-evening date into the next day. Both helpers work in local time to avoid it.

Calendar on its own

The day grid is exported separately for inline use — in a panel, or as the body of a custom popover. It is transparent by design, so whatever surface it sits on supplies the material.

September 2026
<Calendar mode="single" selected={date} onSelect={setDate} />