shadcn.io is not affiliated with official shadcn/ui
Leave Date Input Form Block for React Aria
A date field you type or pick: month, day and year segments plus a calendar button, an impossible day named as it is typed, a soft year-end freeze notice, the date read back in words and two quick chips.
Installation
Overview
Ask for one day with this date input form for React with React Aria. A compact header sits over a hairline frame with a plus mark on each corner: a label-and-field row (the rules and the label at the start, the field at the end) and an action foot. The field is the Date Input (month, day and year segments) with an outline Button beside it that opens a Calendar in a popover; typing and picking write into the same segments, and two chips fill it from now ("Next Monday", "In two weeks"). A day before 14 October 2026 is refused, a day typed past the end of the month is named as it is typed ("31 November does not exist. The field kept 30."), and a start inside the year-end freeze (21 December to 2 January) is allowed but says it needs a manager's approval. The date is read back in words under the field ("Monday 19 October 2026"). It's built from Date Input, Calendar, Popover, Button, Shine Button and Guides, with a CSS entrance only. For leave, bookings, deadlines and any form that takes one date.
FAQ
How do I change the earliest day and the freeze?
Edit the constants at the top: AS_OF (the day the block treats as now, which closes every earlier day in the calendar and in the check), and FREEZE_FROM and FREEZE_TO (the first and last day of the window). problemOf is the rule that refuses a date and noticeOf the one that only warns, so a new rule is one more line in either. Both read ISO days, so no time zone can move a date.
How does it name a day like 31 November?
The date input never holds an impossible date: a day past the month's end is quietly shown as the month's last day. The block follows the digits typed into the day segment and, when they make a day past the month's end, says so under the field and in the foot, naming the month and the day the field kept. It needs the month to be set first, which the month-first order guarantees, and it judges February against a leap year until the year is typed. A day picked on the calendar can't be impossible.
How do the calendar, the chips and the segments stay in step?
The segments report a date once all three are filled, and the calendar and chips write a date back into them through a small child component inside the date input (DateBridge), so there is one value, kept as an ISO day. Picking a day closes the calendar and puts focus back on its button. The calendar can't pick a day before 14 October. Escape closes the calendar with focus back on the button.
When does the field check?
When focus leaves the field, and when you press Request leave, which also moves focus to the month segment if the date is refused. A typed impossible day is the one thing named at once. The freeze notice appears as soon as a valid date inside the window is in the field, because it is not a mistake.
What stays local, and how do I connect it for real?
Nothing is requested: the foot says "Requested Mon 19 Oct. Nothing was sent." To make it real, send the ISO day from the submit handler, apply the freeze and the calendar rules on your server too, and show the answer in the same status line. The two chips are worked out from AS_OF, so replace it with the day your server reports.
How do I connect it to react-hook-form or TanStack Form?
Store the ISO day as the form's value. With react-hook-form, use Controller, call field.onChange with the ISO day from the date input's value handler, and put problemOf in a resolver. With TanStack Form, validate with onBlur and return the message problemOf gives.