Date Picker

A date field that opens a calendar under it. Its trigger is Select's, so the two line up in a form: the same four sizes, label, helper text and focus ring. It holds one day or a range, and shows it as Jan 20, 2026, or 20 يناير 2026 on an Arabic page.

Installation

bash
npx @smicolon/fasla-ui@latest add date-picker

Run init once per project before adding components. See Installation.

Preview

Delivery date
We deliver between 9am and 5pm.

Sizes

Four sizes: xs is 28px tall, sm 32px, md 36px and lg 40px, the same as Select. Only the height and the gap between the icon and the text change: the padding is 12px and the calendar icon 16px at every size.

States

Focus is a real state, not a prop: tab to the field, or open it. The trigger draws the focus ring on keyboard focus and for as long as the calendar is open. disabled dims the whole field to half opacity, its label and helper text included.

Enabled
Disabled

Range

mode="range" holds a start and an end, joined with a dash. The calendar stays open after the first press and closes once both ends are chosen. Set numberOfMonths to 2 to open two months.

Stay
Check-in and check-out.

Beside a Select

The label, the note at the end of the label row and the helper text work as on Select, and the trigger is Select's own, so a Date Picker and a Select line up side by side at every size.

City

Optional

Where we deliver your order.

Delivery date
Optional
We deliver between 9am and 5pm.

The calendar

The calendar opens 4px under the trigger, lined up with its start edge, and flips above it when there is no room below. Focus moves to the chosen day, or today, so the arrow keys work at once. Choosing a day closes it; Escape closes it too, and returns focus to the trigger.

In a form

Give the field a name and it submits with its form: a day as 2026-01-20, and a range as 2026-01-20/2026-02-09. Pass Calendar props through calendarProps, such as month and year menus for a date of birth.

Date of birth
Your date of birth is used to calculate your age.

Accessibility

  • The trigger is a button that opens a dialog. It is named by its label and its value together, so a screen reader says both, and its helper text is read with it.
  • Enter or Space opens the calendar and focuses its chosen day. The calendar's own keys then work: arrows, Page Up and Page Down, Home and End. Escape closes it and returns focus to the trigger.
  • label names the trigger. Without one, pass aria-label. Pressing the label moves focus to the trigger without opening the calendar.
  • Direction is inherited from dir, never a prop. The calendar takes the page's direction, language and font from the trigger, though it renders at the end of body.

Props

DatePicker

PropTypeDefaultDescription
mode"single" | "range""single"One day, or a range from one day to another.
valueDate | DateRange | null—The chosen day, or the range with mode="range". Use it with onValueChange.
defaultValueDate | DateRange | null—The value chosen on first render.
onValueChange(value) => void—Called with the new day, or the range at each end.
placeholderReactNode—Text shown while no date is chosen. Defaults to Pick a date, or its Arabic on an Arabic page.
size"xs" | "sm" | "md" | "lg""md"Field height: 28, 32, 36 or 40px.
numberOfMonthsnumber1How many months the calendar shows side by side.
calendarPropsCalendarProps—More Calendar props: disabled days, the shown month, month and year menus, radius.
labelReactNode—The field's label, linked to the trigger for you.
aria-labelstring—The trigger's accessible name when there is no visible label.
aria-labelledbystring—The id of an element that names the trigger, in place of label.
labelEndReactNode—A short note at the end of the label row, such as Optional.
descriptionReactNode—Helper text under the field. A screen reader reads it with the field.
descriptionEndReactNode—A note at the end of the helper row.
disabledbooleanfalseDisables the field and dims the whole group.
namestring—The name the value is submitted under.
formstring—The id of the form the value is submitted with, when the field sits outside it.
idstring—The trigger's id.
openboolean—Opens or closes the calendar, when you control it.
defaultOpenbooleanfalseOpens the calendar on first render.
onOpenChange(open) => void—Called when the calendar opens or closes.
collisionAvoidance{ side, align, fallbackAxisSide }—How the calendar avoids the window edge. By default it flips above the trigger when there is no room below.
containerHTMLElement—Where the calendar renders. Default body.
classNamestring—Extra CSS classes for the field group.

Usage

One day

tsx
import { DatePicker } from "@/components/ui/date-picker"

export function DeliveryDate() {
  return (
    <DatePicker
      name="delivery"
      label="Delivery date"
      description="We deliver between 9am and 5pm."
      calendarProps={{ disabled: { before: new Date() } }}
    />
  )
}

A range

tsx
"use client"

import * as React from "react"
import { DatePicker, type DateRange } from "@/components/ui/date-picker"

export function StayDates() {
  const [stay, setStay] = React.useState<DateRange | null>(null)
  return (
    <DatePicker
      mode="range"
      numberOfMonths={2}
      label="Stay"
      value={stay}
      onValueChange={setStay}
    />
  )
}

In a form

tsx
import { Button } from "@/components/ui/button"
import { DatePicker } from "@/components/ui/date-picker"

export function BirthDateForm() {
  return (
    <form action="/profile" className="flex flex-col items-start gap-8">
      <DatePicker
        name="dob"
        label="Date of birth"
        description="Your date of birth is used to calculate your age."
        calendarProps={{
          captionLayout: "dropdown",
          startMonth: new Date(1926, 0),
          endMonth: new Date(),
        }}
      />
      <Button type="submit">Submit</Button>
    </form>
  )
}