Date picker
Single dates and ranges with presets, in a popover calendar.
Playground
The calendar inside every picker, shown inline so each prop is visible. Today is fixed to Sep 29, 2026. Use the arrow keys on a focused day to move by day and week.
Sep 26 – Oct 3, 2026, 8 days
Props
1import { Calendar } from "@/components/ui/calendar"2import type { DateRange } from "react-day-picker"34const [range, setRange] = useState<DateRange | undefined>()56<Calendar7 mode="range"8 selected={range}9 onSelect={setRange}10 today={new Date(2026, 8, 29)}11/>States
The trigger button in every state. Errors name the rule and the conflicting date, so the fix is obvious.
- Kick-off date
Weekdays only
Defaultplaceholder - Kick-off date
Weekdays only
Hover:hover - Kick-off date
Weekdays only
Focus:focus-visible - Kick-off date
Weekdays only
Loadingaria-busy - Kick-off date
Locked after the contract is signed
Disableddisabled - Contract end
End date must be after start date (Oct 19, 2026).
Erroraria-invalid - Kick-off date
Available, a Monday
Successvalid pick
Examples
Working pickers in popovers: a single date with past days, weekends and anything beyond 90 days disabled; a range with presets (two months on desktop, one on phones); and paired start and end fields that validate each other.
Weekdays only, from today to 90 days ahead.
Sep 23 – Sep 29, 2026, 7 days
End date must be after start date (Oct 19, 2026).
Keyboard
- Open the picker or select the focused day
- EnterorSpace
- Previous or next day
- ←or→
- Previous or next week
- ↑or↓
- Previous or next month
- Page UporPage Down
- Start or end of the week
- HomeorEnd
- Close and return focus to the trigger
- Esc
Semantics and ARIA
- The trigger is a real button with a visible label; its text is the formatted date or the placeholder, so the current value is announced.
- The clear button sits next to the trigger, never inside it, and says what it clears:
aria-label="Clear date, Oct 5, 2026". - The calendar is a
gridfrom react-day-picker; focus moves to the selected day (or today) when it opens, and disabled days are skipped and markedaria-disabled. - Invalid triggers set
aria-invalidandaria-describedbypointing at the error text. - Range presets are buttons with
aria-pressed, and the selected length is announced through a polite live region.