Siteplane UI

Date Field

Edit a date with keyboard segments or a calendar.

Markdown
10012026

Printed on the invoice and used to calculate the due date.

"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

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

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/button @siteplane/form @siteplane/input

Field is installed with Input.

Usage

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.

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

mmddyyyy

We deliver Monday to Friday.

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

10052026
01012026

Set by your workspace admin.

mmddyyyy
Enter an end date.
"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.

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

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

Component properties
PropTypeDefaultDescription
aria-labelstringunsetAccessible name when no FieldLabel or aria-labelledby names the field.
aria-labelledby / aria-describedbystringunsetExternal label and help ids; inside a Field, FieldLabel, FieldDescription and FieldError are connected automatically.
aria-invalidAriaAttributes[aria-invalid]unsetExternal invalid state; while set, it replaces the built-in date checks.
value / defaultValueDate | null (value only)uncontrolled / unsetControlled or initial date; null is an empty controlled value.
onValueChange(value: Date | undefined) => voidunsetReports a completed date, or undefined when cleared.
disabledbooleanfalsePrevents editing/opening; a disabled Field also disables the field.
classNamestringsegment widthOuter 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.
localestringcontextFormatting locale, not time zone.
calendarPropsCalendarOptionsunsetThe calendar options below: defaultMonth, disabled, startMonth, endMonth and todayDate.
readOnly / requiredbooleanfalseRead-only field on the regular surface; required blocks form submission while empty, focuses the field and shows the required message in FieldError.
sizesm | md | lg | xlprovider / lgControl size.
textSizesm | baseunsetField text size; unset is 14px, or 16px at xl, like Input.
variantdefault | elevated | inset | softdefaultInput surface.
formstringunsetAssociated form ID.
namestringunsetHidden serialized input name; a Field name takes precedence.
iconModeaction-end | decorative-start | noneaction-endCalendar button, decorative leading icon or no icon.
openPickerLabelstringOpen date calendarCalendar button name.
incompleteLabelstringEnter a complete date.The error of a required field while its date is incomplete, shown by FieldError.
profiledefault | input-groupdefaultinput-group renders only the date segments, without surface, calendar button or popover, for use inside your own InputGroup.
insetStartbooleanfalseOnly with profile input-group: adds space before the day segment.

size and variant use the same scale and surfaces as Input; see Input sizes and variants.

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

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.