# Date Picker

Choose a date from a button-triggered calendar.

Due date picker

[Complete TSX file](/examples/date-picker/default.tsx)

```tsx
"use client";

import { DatePicker } from "@/siteplane/components/date-picker";
import { Field, FieldLabel } from "@/siteplane/components/field";

export default function Example() {
  return (
    <Field className="w-full max-w-sm">
      <FieldLabel>Due date</FieldLabel>
      <DatePicker className="w-full" />
    </Field>
  );
}
```

## Installation

Complete [one-time setup](/docs/installation/) first.

```bash
pnpm dlx shadcn@4.18.0 add @siteplane/date-picker
```

With npm: `npx shadcn@4.18.0 add @siteplane/date-picker`. [Registry JSON](/registry/date-picker.json) lists source files and dependencies.

For this page’s examples, also install:

```bash
pnpm dlx shadcn@4.18.0 add @siteplane/input
```

Field is installed with Input.

## Usage

```tsx
import { DatePicker } from "@/siteplane/components/date-picker";

<DatePicker aria-label="Meeting date" />;
```

## Controlled value

Pass `value` and `onValueChange` to keep the date in React state. Store `null` for no date, because `undefined` makes the picker uncontrolled.

Controlled meeting date

[Complete TSX file](/examples/date-picker/controlled.tsx)

```tsx
"use client";

import { DatePicker } from "@/siteplane/components/date-picker";
import { useState } from "react";

export default function Example() {
  const [value, setValue] = useState<Date | null>(new Date(2026, 8, 7));
  return (
    <DatePicker
      aria-label="Meeting date"
      value={value}
      onValueChange={(next) => setValue(next ?? null)}
    />
  );
}
```

## Unavailable dates

Use `calendarProps.disabled` to block past dates and weekends; they can't be chosen in the calendar. Check the date again on the server.

Weekday appointment date

[Complete TSX file](/examples/date-picker/constraints.tsx)

```tsx
"use client";

import { DatePicker } from "@/siteplane/components/date-picker";
import {
  Field,
  FieldDescription,
  FieldLabel,
} from "@/siteplane/components/field";
import { useEffect, useState } from "react";

export default function Example() {
  // Read today in the browser once the example mounts. A page rendered on a
  // server or at build time would otherwise keep the day it was rendered.
  const [today, setToday] = useState<Date>();
  useEffect(() => setToday(new Date()), []);
  return (
    <Field className="w-full max-w-sm">
      <FieldLabel>Appointment date</FieldLabel>
      <DatePicker
        calendarProps={{
          disabled: [{ before: today }, { dayOfWeek: [0, 6] }],
        }}
        className="w-full"
      />
      <FieldDescription>
        Appointments are available Monday to Friday.
      </FieldDescription>
    </Field>
  );
}
```

## Custom format

`formatValue` replaces the trigger text, including the empty state, so `placeholder` is not used when it is set. It changes only the display; `onValueChange` still receives a `Date`.

Long date format

[Complete TSX file](/examples/date-picker/format.tsx)

```tsx
"use client";

import { DatePicker } from "@/siteplane/components/date-picker";

const longDate = new Intl.DateTimeFormat("en-US", { dateStyle: "full" });

export default function Example() {
  return (
    <DatePicker
      aria-label="Launch date"
      defaultValue={new Date(2026, 9, 14)}
      formatValue={(date) =>
        date ? longDate.format(date) : "Pick a launch date"
      }
    />
  );
}
```

## Disabled and invalid

A disabled `Field` also disables the picker. For an error from your application, set `invalid` on `Field` and explain the problem in a `FieldError`; the trigger shows the invalid border and state. DatePicker has no read-only mode; use DateField for a read-only date.

Disabled and invalid date pickers

[Complete TSX file](/examples/date-picker/states.tsx)

```tsx
"use client";

import { DatePicker } from "@/siteplane/components/date-picker";
import { Field, FieldError, FieldLabel } from "@/siteplane/components/field";

export default function Example() {
  return (
    <div className="grid w-full max-w-sm gap-4">
      <Field disabled>
        <FieldLabel>Review date</FieldLabel>
        <DatePicker className="w-full" defaultValue={new Date(2026, 9, 14)} />
      </Field>
      <Field invalid>
        <FieldLabel>Due date</FieldLabel>
        <DatePicker className="w-full" />
        <FieldError match={true}>Choose a due date.</FieldError>
      </Field>
    </div>
  );
}
```

## API reference

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| aria-label | string | Choose date | Accessible name when no FieldLabel or aria-labelledby names the picker. |
| aria-invalid | AriaAttributes[aria-invalid] | unset | External invalid state; the trigger shows the invalid border and aria-invalid. |
| value / defaultValue | Date \| null (value only) | uncontrolled / unset | Controlled or initial date; null is an empty controlled value. |
| onValueChange | (value: Date \| undefined) => void | unset | Reports the selected date. |
| disabled | boolean | false | Prevents opening; a disabled Field also disables the picker. |
| required | boolean | false | Blocks form submission while no date is chosen. |
| form | string | unset | Associated form ID. |
| name | string | unset | Submits the date as YYYY-MM-DD under this name, empty while no date is chosen; a Field name takes precedence. |
| className | string | unset | Outer control styling. |
| size | sm \| md \| lg \| xl | provider / lg | Control size, the same as the other date and time fields. |
| textSize | sm \| base | unset | Trigger text size; unset is 14px, or 16px at xl, like Input. |
| locale | string | context | Formatting locale, not time zone. |
| calendarProps | CalendarOptions | unset | The calendar options below: defaultMonth, disabled, startMonth, endMonth and todayDate. |
| placeholder | ReactNode | Choose date | Empty trigger content; not used when formatValue is set. |
| formatValue | (Date \| undefined) => ReactNode | locale medium date | Custom trigger content, also for the empty state. |
| aria-describedby / aria-labelledby | string | unset | External help and label ids; inside a Field, FieldLabel, FieldDescription and FieldError are connected automatically. |

### Calendar options

`calendarProps` takes `CalendarOptions`, the type of the same options on [Calendar](/docs/components/calendar/#calendar-options). `startMonth` and `endMonth` (Date, unset) limit the calendar to the first and last allowed months. `disabled` (unset) makes days unavailable with one matcher or a list of matchers, each a Date, a `(date: Date) => boolean` test or a rule with `after`, `before`, `from`, `to` and `dayOfWeek` (0 Sunday through 6 Saturday); `true` disables the whole calendar. `defaultMonth` (Date, unset) sets the month the calendar shows when it opens; while it is set, it also wins over the month of the selected date. `todayDate` (Date, the device's date) sets the day the calendar marks as today; without a selected date, the calendar opens on its month.

Exported types: `DatePickerProps`. These describe the contracts above.

[Read the installed source](/source/date-picker).

## Usage notes

Values use native Date calendar fields in the local runtime time zone. Locale changes formatting, not the zone. The trigger is a button that opens the calendar; there are no editable segments. Inside a Field, FieldLabel names the picker and FieldDescription and FieldError describe it; outside a Field, `aria-label` (default "Choose date") or `aria-labelledby` names it. With `name`, DatePicker submits the date as `YYYY-MM-DD`, like DateField. Only the props listed above are supported, and DatePicker renders its own trigger, popover and calendar; it accepts no children or ref.
