Components · Date, time and scheduling
DatePicker
A date field: type in the locale’s format or pick from a calendar popover, with presets, min/max and Field errors.
Due date with quick picks
Type a date or pick one. Value: none
import { useState } from 'react';
import { Field, FieldLabel, FieldDescription } from '@rookcom/monarch-react';
import { DatePicker } from '@rookcom/monarch-react/scheduling';
const TODAY = '2026-11-04';
const PRESETS = [
{ label: 'Today', days: 0 },
{ label: 'Tomorrow', days: 1 },
{ label: 'In a week', days: 7 },
{ label: 'In two weeks', days: 14 },
];
function DueDate() {
const [value, setValue] = useState<string | null>(null);
return (
<Field>
<FieldLabel>Due date</FieldLabel>
<DatePicker
locale="en-GB"
today={TODAY}
presets={PRESETS}
value={value}
onValueChange={setValue}
/>
<FieldDescription>Type a date or pick one. Value: {value ?? 'none'}</FieldDescription>
</Field>
);
}
<DueDate />Usage examples
Min, max and an error
import { Field, FieldLabel } from '@rookcom/monarch-react';
import { DatePicker } from '@rookcom/monarch-react/scheduling';
const TODAY = '2026-11-04';
<Field>
<FieldLabel>Renewal date</FieldLabel>
<DatePicker
locale="en-GB"
today={TODAY}
min="2026-11-01"
max="2027-10-31"
defaultValue="2027-12-01"
/>
</Field>Sizes and formats
import { DatePicker } from '@rookcom/monarch-react/scheduling';
const TODAY = '2026-11-04';
<DatePicker
aria-label="Start"
size="sm"
locale="en-US"
today={TODAY}
defaultValue="2026-11-04"
/>
<DatePicker
aria-label="Published"
size="lg"
locale="de-DE"
today={TODAY}
defaultValue="2026-11-04"
formatOptions={{ dateStyle: 'medium' }}
/>
<DatePicker
aria-label="Locked"
disabled
locale="en-GB"
today={TODAY}
defaultValue="2026-11-04"
/>Inside a dialog
import { Dialog, DialogContent, DialogTrigger, DialogTitle, DialogBody, Button, Field, FieldLabel } from '@rookcom/monarch-react';
import { DatePicker } from '@rookcom/monarch-react/scheduling';
const TODAY = '2026-11-04';
function InDialog() {
return (
<Dialog>
<DialogTrigger asChild>
<Button>Schedule review</Button>
</DialogTrigger>
<DialogContent size="sm">
<DialogTitle>Schedule review</DialogTitle>
<DialogBody>
<Field>
<FieldLabel>Review date</FieldLabel>
<DatePicker locale="en-GB" today={TODAY} min={TODAY} />
</Field>
</DialogBody>
</DialogContent>
</Dialog>
);
}
<InDialog />Purpose
Lets people enter one calendar date in a form, by typing it the way their locale writes dates or by picking it from a calendar. The value is an ISO YYYY-MM-DD string with no time zone, so it never shifts by a day. Replaces native type="date" inputs, which render differently in every browser and cannot show presets. Import from @rookcom/monarch-react/scheduling.
Use when
- Due dates, start dates, birthdays, renewal and expiry dates in forms and editors.
- A date with quick picks such as Today, Tomorrow and In a week.
Avoid when
- A date and a time: use DateTimePicker.
- A period: use DateRangePicker.
- Showing a date read-only: format it with RelativeTime or plain text.
Design principles
- Typing is first-class: the field accepts the locale’s numeric order, month names, ISO and digits without separators.
- Invalid text is kept so it can be fixed, and explained with the expected format; the value only changes to a valid date.
- The calendar is the same MiniMonth grid everywhere, with the same keyboard model.
- No time zones: a date is a calendar date.
API reference
import { DatePicker } from '@rookcom/monarch-react/scheduling';| Export | Kind | Rendering | Root class |
|---|---|---|---|
DatePicker | component | client | .mn-date-picker |
The ref goes to the text <input>; native input props (id, aria-*, onBlur, autoFocus, …) go there too. className styles the wrapper.
With name, a hidden input submits the ISO value; the visible input has no name.
Generated from the TypeScript declarations of packages/react/src/components/scheduling/date-picker.
Properties
value/onValueChange (controlled) or defaultValue; onValueChange receives a valid ISO date or null, never invalid text.
presets: { label, days } relative to today or { label, date } fixed.
onValidationChange reports invalid, before-min, after-max, unavailable or null; set showErrors={false} to render your own FieldError.
messages translates every string, including the error texts.
DatePicker
A date field: type a date in the locale's format, or pick it from a calendar popover with optional quick picks. The value is an ISO `YYYY-MM-DD` string with no time or time zone. Validates min, max and disabled dates, and reports errors through Field.
| Prop | Type | Description |
|---|---|---|
children | ReactNode | |
className | string | Class for the outer wrapper (the input itself is `.mn-date-picker__input`). |
clearable | boolean | Show a clear button while there is a value. Default true. |
defaultValue | string | null | Initial date (uncontrolled). |
disabled | boolean | |
formatOptions | Intl.DateTimeFormatOptions | Display format of the text. Default: the locale's numeric date (`04/11/2026`). |
getDayInfo | ((date: IsoDate) => MiniMonthDayInfo | undefined) | Dots and descriptions for calendar days (see MiniMonth). |
id | string | |
invalid | boolean | Marks the field invalid. Inside a Field, the Field's `invalid` is used. Built-in errors also set it. |
isDateDisabled | ((date: IsoDate) => boolean) | Blocks extra dates (weekends, holidays). |
locale | string | BCP 47 locale for typing, display and the calendar. Default: the runtime's. Pass it for server rendering. |
max | string | Latest allowed date. |
messages | Partial<DatePickerMessages> | Text for translation. |
min | string | Earliest allowed date. Earlier typed dates show an error; earlier days are disabled. |
onOpenChange | ((open: boolean) => void) | Called when the calendar opens or closes. |
onValidationChange | ((error: DateValidationError | null) => void) | Called when the built-in validation result changes (`null` when valid). |
onValueChange | ((value: IsoDate | null) => void) | Called with a new valid date, or `null` when cleared. Never called with invalid text. |
open | boolean | Whether the calendar is open (controlled). |
presets | readonly DatePickerPreset[] | Quick picks such as Today and Tomorrow, shown beside the calendar. |
showErrors | boolean | Render the built-in error message under the field. Default true. Turn off to show your own FieldError. |
showWeekNumbers | boolean | Show ISO week numbers in the calendar. |
size | "sm" | "md" | "lg" | Control height. Default `md`. |
today | string | Today's date. Default: the device's. |
value | string | null | The date (controlled), `YYYY-MM-DD`, or `null` for none. |
weekStartsOn | Weekday | First day of the calendar week, 0 (Sunday) to 6. Default: from the locale. |
Variants
| Prop | Values | Description |
|---|---|---|
size | 'sm''md''lg' | Control height, as Input. Default md. |
presets | 'none''list' | Quick picks beside the calendar (above it on phones). |
clearable | 'true''false' | A clear button while there is a value. Default true. |
formatOptions | 'numeric (default)''Intl options' | How the value is written in the field, e.g. { dateStyle: "medium" }. |
States
| State | Exposed as | Behaviour |
|---|---|---|
| Empty | – | Shows the format hint as placeholder, e.g. DD/MM/YYYY. |
| Open | data-state="open" on the calendar button | The calendar popover is showing. |
| Invalid | aria-invalid="true", data-invalid | Unreadable text, a date outside min/max or a disabled date; a message explains why. |
| Disabled | disabled, data-disabled | From the prop or the Field; the calendar cannot open. |
| Read-only | readonly | The value can be read and copied; the calendar button is disabled. |
Interaction behaviour
- Typed text is read on blur and on Enter. A valid date is re-formatted in the locale; anything else stays as typed with an error.
- The calendar button or ↓ in the field opens the calendar, focused on the value (or today). Picking a day or a preset closes it and returns focus to the field.
- Emptying the field or pressing the clear button sets the value to
null. - The calendar is a modal popover: it traps focus while open and works inside Dialog and Sheet; Escape closes only the calendar.
Keyboard support
| Keys | Action |
|---|---|
| Enter (in the field) | Read the typed date. |
| ↓ (in the field) | Open the calendar. |
| Arrows, Home/End, Page Up/Down | Move in the calendar (see MiniMonth). |
| Enter / Space (in the calendar) | Pick the focused day and close. |
| Escape | Close the calendar without changing the value. |
| Tab | Moves between the field, the clear and calendar buttons; inside the calendar, between presets, month buttons and the grid. |
Accessibility
- Role
- textbox, with a button that opens a dialog containing a grid
- Accessible name
- From FieldLabel (or
aria-label). The calendar button is named "Choose date" or "Change date, <full date>"; the popover "Choose date".
- The calendar button has
aria-haspopup="dialog"andaria-expanded. - Errors are rendered under the field, linked by
aria-describedbyand announced politely; the field getsaria-invalid. - Inside a Field,
aria-required, the description anddisabledare wired automatically. - The format hint is the placeholder and is repeated in the error text, so it is not the only cue.
- Presets are buttons with
aria-pressedfor the current value.
Responsive behaviour
- The field fills its container; the popover never exceeds the viewport minus 1rem.
- Below 576px, presets move above the calendar as wrapping chips.
- Larger buttons and presets on touch screens.
Theme support
- Uses Input’s tokens for the field and MiniMonth’s for the calendar, so every theme and more-contrast mode applies.
- Transitions are removed under
prefers-reduced-motion: reduce.
Do and don't
Do
- Put it in a Field with a FieldLabel, and pass
localefrom the user’s settings. - Store and send the ISO string as is; format it for display with Intl and
timeZone: "UTC". - Offer presets for the common choices in task and deadline editors.
Don't
- Do not build a
Datefrom the value in local time (new Date("2026-11-04")is UTC midnight and shows the previous day west of UTC). - Do not show overdue state only by colouring the field; add a Badge or text.
- Do not use it for a date with a time: use DateTimePicker.
Related components
- DateTimePicker: A date and a time.
- DateRangePicker: A start and end date.
- MiniMonth: The calendar grid on its own.
- Field: Label, description and errors.
Dependencies
The popover is Monarch Popover (Radix), opened as modal.
Testing coverage
- Labelled textbox wired to Field (required, description); calendar button with
aria-haspopup; axe clean. - Typed dates in en-GB and en-US order, on blur and Enter; month names; re-formatting.
- Invalid text kept with an error and
aria-invalid, value unchanged,onValidationChange. - min, max and disabled dates rejected with messages naming the bound.
- Calendar opens focused on the value, picks with the keyboard, closes and returns focus; ↓ opens on the typed month; Escape.
- Presets relative to today, disabled outside the bounds.
- Clear button and emptying the text; controlled value; hidden input for forms; disabled and read-only.
- Inside Dialog: the calendar takes focus and Escape closes only the calendar.
| Check | Covered |
|---|---|
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | Visual baselines: /__gallery/date-picker and the open calendar (overlays/date-picker-*.png); Cross-browser axe: /components/date-picker |
Tests: packages/react/src/components/scheduling/date-picker