Siteplane UI

Date Picker

Choose a date from a button-triggered calendar.

Markdown
"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

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

Complete the one-time setup first. Registry JSON lists source files and dependencies.

For this page’s examples, also install:

pnpm dlx shadcn@4.18.0 add @siteplane/input

Field is installed with Input.

Usage

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.

"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.

Appointments are available Monday to Friday.

"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.

"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.

Choose a due date.
"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

Component properties
PropTypeDefaultDescription
aria-labelstringChoose dateAccessible name when no FieldLabel or aria-labelledby names the picker.
aria-invalidAriaAttributes[aria-invalid]unsetExternal invalid state; the trigger shows the invalid border and aria-invalid.
value / defaultValueDate | null (value only)uncontrolled / unsetControlled or initial date; null is an empty controlled value.
onValueChange(value: Date | undefined) => voidunsetReports the selected date.
disabledbooleanfalsePrevents opening; a disabled Field also disables the picker.
requiredbooleanfalseBlocks form submission while no date is chosen.
formstringunsetAssociated form ID.
namestringunsetSubmits the date as YYYY-MM-DD under this name, empty while no date is chosen; a Field name takes precedence.
classNamestringunsetOuter control styling.
sizesm | md | lg | xlprovider / lgControl size, the same as the other date and time fields.
textSizesm | baseunsetTrigger text size; unset is 14px, or 16px at xl, like Input.
localestringcontextFormatting locale, not time zone.
calendarPropsCalendarOptionsunsetThe calendar options below: defaultMonth, disabled, startMonth, endMonth and todayDate.
placeholderReactNodeChoose dateEmpty trigger content; not used when formatValue is set.
formatValue(Date | undefined) => ReactNodelocale medium dateCustom trigger content, also for the empty state.
aria-describedby / aria-labelledbystringunsetExternal 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. 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.

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.