Skip to content
TopClass UI

Date picker

One day picked from a month. The calendar loads on its first open, and every month name and digit comes from Intl.

client - size not measured yet, lazy

Date picker

client - size not measured yet, lazy

One day picked from a month. The calendar loads on its first open, and every month name and digit comes from Intl.

Install

Try it

size
invalid
fullWidth
placement
firstDayOfWeek

The code

date-picker.tsx
import { DatePicker } from 'topclassui/date-picker'

<div className="grid justify-items-start gap-1.5">
  <label htmlFor="date-picker-playground" className="text-sm text-body">Check-in</label>
  <DatePicker id="date-picker-playground" defaultValue="2026-09-22" />
</div>

The value is an ISO date string, YYYY-MM-DD; the shown format comes from the provider's locale.

Every state

3 states
Pick a weekday.

Import from the package

topclassui/date-picker
import { DatePicker } from 'topclassui/date-picker'

Install once:npm i topclassui@beta zod

Examples

  • value and defaultValue are an ISO date or null; a string that is not a real day, such as 2026-02-30, counts as null.
  • With a name, a hidden input sends the ISO date, and an empty value with nothing chosen. Before the page hydrates it sends the default.
  • minDate and maxDate are ISO dates; a day outside them is dimmed and cannot be chosen, and the month buttons stop at them.
  • The week starts on firstDayOfWeek, 1 (Monday) to 7 (Sunday); left out, on the locale's first day.
  • The calendar is a chunk of its own, loaded on the first open. The grid is always Gregorian, with no date library.
basic
in-field
Rooms are ready from 15:00
limits
week-start
form
sizes
In Arabic, right to left

Props

The props of DatePicker
PropTypeDefaultLimitsDescription
aria-labelstring--The name read out when no visible label names the control.
aria-labelledbystring--The id of the visible text that names the control.
idstring--The id a <label htmlFor> points at.
classNamestring--On the trigger, merged last, so a consumer class wins a conflict with its own.
containerBase.Portal.Props['container']--Where the calendar mounts; <body> by default.
defaultValuestring | null--The day of the first render, and the one a form reset brings back.
firstDayOfWeeknumber, min 1, max 7-min 1, max 7DatePicker: the first column, 1 (Monday) to 7 (Sunday), rounded and clamped; left out, the locale's.
formstring--The id of a form elsewhere on the page that the form input belongs to.
fullWidthbooleanfalse-DatePicker: the box fills the width of its parent instead of taking the width of an Input.
invalidbooleanfalse-DatePicker: marks the date as wrong: sets aria-invalid on the trigger, whose line turns to danger and thickens.
maxDatestring"9999-12-31"-DatePicker: the last day that can be chosen, clamped like minDate and raised to it when below.
minDatestring"0001-01-01"-DatePicker: the first day that can be chosen, an ISO date clamped to 0001-01-01 to 9999-12-31.
namestring--The form input's name: the form sends name=YYYY-MM-DD, and name= when empty.
onValueChange((value: string) => void)--Called with the ISO date of the day chosen. Client-only.
placeholderstring--The text of an empty trigger. Left out, the provider's datePicker.placeholder.
placementtop | top-start | top-end | bottom | bottom-start | bottom-end | start | start-top | start-bottom | end | end-top | end-bottom"bottom-start"-DatePicker: the side and alignment the calendar opens at; start and end mirror in Arabic.
refRef<HTMLButtonElement>--The trigger, a <button aria-haspopup="dialog">.
requiredboolean--Blocks an empty submit, with or without script; the trigger is described as required.
sizexs | sm | md | lg | xl | custom"md"-DatePicker: the height, the inline padding and the type step of the trigger, and its width with them; custom emits no size classes.
valuestring | null--The chosen day as an ISO date, YYYY-MM-DD, or null. A string that is not a real day counts as null.

Keyboard and accessibility

The keyboard of DatePicker
KeysWhat it does
EnterSpaceOn the button, open the calendar on the chosen day, else on today; in the grid, choose the day and close.
ArrowLeftArrowRightMove a day back or forward.
ArrowUpArrowDownMove a week back or forward.
HomeEndGo to the first or the last day of the row, within the month.
PageUpPageDownMove a month; with Shift, a year. The day stays inside the month.
EscapeCloses the calendar with no change and returns the focus to the button.
TabGoes round previous, next, close and the grid.
  • A name is required: id for a <label htmlFor>, or aria-label, or aria-labelledby. The label names the button and the day is its description.
  • The calendar follows the date picker dialog of the ARIA Authoring Practices; while it is open the focus is in it, named by its month.
  • required describes the button as required, since a button cannot carry aria-required. invalid turns its line to danger and thickens it.

Right to left

Month and day names come in Arabic, the week starts on Saturday in ar, and the calendar lines up with the right edge of the button. ArrowLeft is the next day.

Server and client

A client component: it renders on the server and hydrates in the browser.

Files: index.tsx, client.tsx.

On the chip: client - size not measured yet, lazy.