# Field: Date Picker (category: special)

URL: https://docs.forms.saastro.io/docs/date

<FieldPreview name="date" description="Interactive demo of date picker field" />

## Overview

The date picker field provides a calendar interface for selecting a single date, built on react-day-picker. It renders either a popover-triggered calendar (the default) or an inline calendar, controlled by the `dateType` prop, and supports date constraints through Zod schemas.

## Usage

### Basic Date Picker

```tsx

const config = FormBuilder.create('form')
  .addField('birthdate', (f) =>
    f
      .type('date')
      .label('Date of Birth')
      .placeholder('Select your birth date')
      .required('Date of birth is required'),
  )
  .addStep('main', ['birthdate'])
  .build();
```

### Display Modes (`dateType`)

The `dateType` prop switches between two render modes:

- `'popover'` (default) — an outline button that opens the calendar in a popover. The button shows the selected date (or the placeholder) and the calendar uses dropdown month/year navigation. The popover closes when a date is picked.
- `'simple'` — the calendar is rendered inline, always visible, with no trigger button.

```tsx
// Inline calendar instead of the default popover
.addField('checkIn', (f) =>
  f.type('date')
    .label('Check-in Date')
    .dateType('simple')
    .required()
)
```

### With Date Constraints

```tsx
.addField('appointment', (f) =>
  f.type('date')
    .label('Appointment Date')
    .placeholder('Select a date')
    // Future dates only (via Zod) — z.date() already rejects empty values,
    // so the field is implicitly required
    .schema(z.date().min(new Date(), 'Must be a future date'))
)
```

> **Don't mix `.schema(zodSchema)` with declarative validation methods** like `.required()` or `.regex()` on the same field — calling a declarative method replaces a previously set Zod schema, silently dropping its constraints. Use one approach or the other.

### Default Value

```tsx
.addField('startDate', (f) =>
  f.type('date')
    .label('Start Date')
    .value(new Date())
    .required()
)
```

### JSON Configuration

```json
{
  "type": "date",
  "label": "Date of Birth",
  "placeholder": "Select your birth date",
  "dateType": "popover",
  "schema": { "required": true }
}
```

## Props

| Prop          | Type                                                  | Default     | Description                                  |
| ------------- | ----------------------------------------------------- | ----------- | -------------------------------------------- |
| `type`        | `'date'`                                              | -           | Field type (required)                        |
| `dateType`    | `'simple' \| 'popover'`                               | `'popover'` | Inline calendar (`simple`) or popover button |
| `label`       | `string`                                              | -           | Label text                                   |
| `placeholder` | `string`                                              | `'Pick a date'` | Popover button text when no date is selected |
| `helperText`  | `string`                                              | -           | Help text below field                        |
| `value`       | `Date`                                                | -           | Default date                                 |
| `columns`     | `Partial<Record<Breakpoint, number>>`                 | -           | Grid columns by breakpoint                   |
| `disabled`    | `boolean \| function \| ConditionGroup`               | `false`     | Disable the field                            |
| `hidden`      | `boolean \| function \| ConditionGroup \| Responsive` | `false`     | Hide the field                               |

> **Declared but not yet implemented:** `showTime?: boolean` and `presets?: Array<{ label: string; value: number }>` exist on the field's type (and have builder methods `.showTime()` / `.datePresets()`), but the renderer currently ignores them — setting them has no effect.

## Display Format & Localization

In popover mode, the selected date is shown using the date-fns `'PPP'` format — English by default (e.g. `June 11th, 2026`). The default placeholder is `'Pick a date'`, overridable with the `placeholder` prop or per-locale via the [i18n overlay](/docs/i18n). The date display locale is configurable globally with `setDateLocale`:

```tsx

setDateLocale(es); // 'PPP' now renders "11 de junio de 2026"
```

## Validation

### Required Date

```tsx
.addField('eventDate', (f) =>
  f.type('date')
    .label('Event Date')
    .required('Please select a date')
)
```

### With Zod Constraints

```tsx

const oneYearFromNow = new Date();
oneYearFromNow.setFullYear(oneYearFromNow.getFullYear() + 1);

.addField('deadline', (f) =>
  f.type('date')
    .label('Deadline')
    .schema(z.date()
      .min(new Date(), 'Must be a future date')
      .max(oneYearFromNow, 'Must be within the next year')
    )
)
```

## Styling

### Custom Classes

```tsx
.addField('birthdate', (f) =>
  f.type('date')
    .label('Date of Birth')
    .required()
    .classNames({
      wrapper: 'bg-muted/30 p-4 rounded-lg',
      input: 'border-2 border-primary/20',
      label: 'text-lg font-semibold',
      error: 'text-destructive text-sm',
    })
)
```

### Responsive Layout

```tsx
.addField('eventDate', (f) =>
  f.type('date')
    .label('Event Date')
    .columns({ default: 12, md: 6 })
)
```

### JSON Styling

```json
{
  "type": "date",
  "label": "Date of Birth",
  "wrapper_className": "bg-muted/30 p-4 rounded-lg",
  "input_className": "border-2 border-primary/20",
  "label_className": "text-lg font-semibold",
  "columns": { "default": 12, "md": 6 }
}
```

## Related Fields

- [Date Range](/docs/daterange) - Select a range of dates

## Accessibility

- Built on react-day-picker
- Full keyboard navigation
- Screen reader support
- ARIA labels for calendar navigation
