# Date Field

Edit a date with keyboard segments or a calendar.

Invoice date field

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

```tsx
"use client";

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

export default function Example() {
  return (
    <Field className="w-full max-w-sm">
      <FieldLabel>Invoice date</FieldLabel>
      <DateField defaultValue={new Date(2026, 9, 1)} />
      <FieldDescription>
        Printed on the invoice and used to calculate the due date.
      </FieldDescription>
    </Field>
  );
}
```

## Installation

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

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

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

For this page’s examples, also install:

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

Field is installed with Input.

## Usage

```tsx
import { DateField } from "@/siteplane/components/date-field";

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

## Controlled value

Pass `value` and `onValueChange` to keep the date in React state. `onValueChange` receives `undefined` when the field is cleared; store `null` for an empty controlled value, because `undefined` makes the field uncontrolled.

Controlled meeting date

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

```tsx
"use client";

import { DateField } from "@/siteplane/components/date-field";
import { useState } from "react";

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

## Unavailable dates

Use `calendarProps.disabled` to block past dates and weekends. The calendar doesn't offer these dates, and a typed date that breaks a rule marks the field invalid once it loses focus or the form is submitted; `FieldError` shows the message. Check the date again on the server.

Weekday delivery date

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

```tsx
"use client";

import { DateField } from "@/siteplane/components/date-field";
import {
  Field,
  FieldDescription,
  FieldError,
  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>Delivery date</FieldLabel>
      <DateField
        calendarProps={{
          disabled: [{ before: today }, { dayOfWeek: [0, 6] }],
        }}
      />
      <FieldDescription>We deliver Monday to Friday.</FieldDescription>
      <FieldError />
    </Field>
  );
}
```

## Disabled, read-only and invalid

A disabled `Field` also disables the date field, and `readOnly` shows a date that can't be changed on the regular field surface. For an error from your application, set `invalid` on `Field` and explain the problem in a `FieldError`.

Disabled, read-only and invalid date fields

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

```tsx
"use client";

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

export default function Example() {
  return (
    <div className="grid w-full max-w-sm gap-4">
      <Field disabled>
        <FieldLabel>Start date</FieldLabel>
        <DateField defaultValue={new Date(2026, 9, 5)} />
      </Field>
      <Field>
        <FieldLabel>Contract start</FieldLabel>
        <DateField defaultValue={new Date(2026, 0, 1)} readOnly />
        <FieldDescription>Set by your workspace admin.</FieldDescription>
      </Field>
      <Field invalid>
        <FieldLabel>End date</FieldLabel>
        <DateField />
        <FieldError match={true}>Enter an end date.</FieldError>
      </Field>
    </div>
  );
}
```

## Submission and reset

With `name`, DateField submits a hidden `YYYY-MM-DD` string; the example submits `{ "startDate": "2026-10-05" }`. Changing `locale` changes only the display, and an empty field submits an empty string. With `required`, submitting an empty field is blocked, the field is focused and `FieldError` shows the required message. Native form reset restores `defaultValue`; reset controlled state yourself.

Date field in a form

[Complete TSX file](/examples/date-field/form.tsx)

```tsx
"use client";

import { Button } from "@/siteplane/components/button";
import { DateField } from "@/siteplane/components/date-field";
import { Field, FieldError, FieldLabel } from "@/siteplane/components/field";
import { Form } from "@/siteplane/components/form";

export default function Example() {
  return (
    <Form
      className="grid w-full max-w-sm gap-4"
      onSubmit={(event) => {
        event.preventDefault();
      }}
    >
      <Field>
        <FieldLabel>Start date</FieldLabel>
        <DateField
          className="w-full"
          defaultValue={new Date(2026, 9, 5)}
          name="startDate"
          required
        />
        <FieldError />
      </Field>
      <div className="flex gap-2">
        <Button type="submit">Submit</Button>
        <Button type="reset" variant="outline">
          Reset
        </Button>
      </div>
    </Form>
  );
}
```

## Decorative icon

Use `iconMode="decorative-start"` for dates people usually type, such as a date of birth. The icon is decorative and no calendar opens; `iconMode="none"` removes the icon.

Date of birth field

[Complete TSX file](/examples/date-field/icons.tsx)

```tsx
"use client";

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

export default function Example() {
  return (
    <Field className="w-full max-w-sm">
      <FieldLabel>Date of birth</FieldLabel>
      <DateField iconMode="decorative-start" />
    </Field>
  );
}
```

## API reference

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| aria-label | string | unset | Accessible name when no FieldLabel or aria-labelledby names the field. |
| aria-labelledby / aria-describedby | string | unset | External label and help ids; inside a Field, FieldLabel, FieldDescription and FieldError are connected automatically. |
| aria-invalid | AriaAttributes[aria-invalid] | unset | External invalid state; while set, it replaces the built-in date checks. |
| 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 a completed date, or undefined when cleared. |
| disabled | boolean | false | Prevents editing/opening; a disabled Field also disables the field. |
| className | string | segment width | Outer control styling and width. By default the field is as wide as its segments and the calendar button; a width utility such as w-full or w-64 replaces that. |
| locale | string | context | Formatting locale, not time zone. |
| calendarProps | CalendarOptions | unset | The calendar options below: defaultMonth, disabled, startMonth, endMonth and todayDate. |
| readOnly / required | boolean | false | Read-only field on the regular surface; required blocks form submission while empty, focuses the field and shows the required message in FieldError. |
| size | sm \| md \| lg \| xl | provider / lg | Control size. |
| textSize | sm \| base | unset | Field text size; unset is 14px, or 16px at xl, like Input. |
| variant | default \| elevated \| inset \| soft | default | Input surface. |
| form | string | unset | Associated form ID. |
| name | string | unset | Hidden serialized input name; a Field name takes precedence. |
| iconMode | action-end \| decorative-start \| none | action-end | Calendar button, decorative leading icon or no icon. |
| openPickerLabel | string | Open date calendar | Calendar button name. |
| incompleteLabel | string | Enter a complete date. | The error of a required field while its date is incomplete, shown by FieldError. |
| profile | default \| input-group | default | input-group renders only the date segments, without surface, calendar button or popover, for use inside your own InputGroup. |
| insetStart | boolean | false | Only with profile input-group: adds space before the day segment. |

`size` and `variant` use the same scale and surfaces as Input; see [Input sizes](/docs/components/input/#sizes) and [variants](/docs/components/input/#variants).

### 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. Both apply to picking in the calendar and to typed dates.
`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, and typed segments start from it.

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

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

## Usage notes

Values use native Date calendar fields in the local runtime time zone. Locale changes formatting, not the zone. The field is compact by default, as wide as a date takes; add `className="w-full"` to fill a form column. Inside a Field, FieldLabel names DateField and clicking it focuses the first segment; outside a Field, pass `aria-label` or `aria-labelledby`. Arrow keys change the focused segment. Only the props listed above are supported, and DateField renders its own segments, calendar button and popover; it accepts no children or ref.

The segments read in the direction of the locale, which the arrow keys between them follow too, and are isolated from the text around them. An `en-US` date stays 10/14/2026 in a right-to-left page, while the field keeps the page's direction: its calendar button sits at the page's inline end.
