> Markdown version of [Date Picker](https://vaadin.com/docs/v23/components/date-picker). Section index: [llms.txt](https://vaadin.com/docs/v23/components/llms.txt)

# Date Picker

Date Picker is an input field that allows the user to enter a date by typing or by selecting from a calendar overlay.

**TypeScript** — `date-picker-basic.ts`

```html
<vaadin-date-picker label="Start date"></vaadin-date-picker>
```

**Java** — `DatePickerBasic.java`

```java
DatePicker datePicker = new DatePicker("Start date");
add(datePicker);
```

The date can be entered directly using the keyboard in the format of the current locale or through the date picker overlay. The overlay opens when the field is clicked and/or any input is entered when the field is focused.

## <a id="common-input-field-features"></a>Common Input Field Features

Date Picker includes all Text Field and shared input field features.

- [See Text Field features](https://vaadin.com/docs/v23/components/text-field.md)

- [See shared input field features](https://vaadin.com/docs/v23/components/input-fields.md)

## <a id="date-format"></a>Date Format

Date Picker can be configured to display dates and parse user input in a specific format.

### <a id="using-java-locales-java-only"></a>Using Java Locales (Java Only)

By default, Date Picker displays and parses dates using the user’s locale ([reference](https://vaadin.com/docs/v23/advanced/i18n-localization.md#locale-selection-for-new-session)). Alternatively, setting a specific locale ensures that all users consistently see the same format.

`DatePickerLocale.java`

```java
Locale finnishLocale = new Locale("fi", "FI");

DatePicker datePicker = new DatePicker("Select a date:");
datePicker.setLocale(finnishLocale);
```

The date format that’s used based on the locale depends on the specific browser implementation and might not be reliable when expecting a specific pattern. For finer-grained control over the date format, see the next section.

### <a id="using-custom-date-formats-java-only"></a>Using Custom Date Formats (Java Only)

Date Picker allows you to configure a custom date format pattern that defines exactly how dates should be displayed and how user input should be parsed. Additional parsing formats can be provided to support entering dates in different formats. Parsing is first tried with the primary format, then by additional parsing formats in the order that they are provided.

`DatePickerCustomFormat.java`

```java
// Setup date picker with a single custom format `yyyy-MM-dd` for
// displaying dates and parsing user input
// Custom date formats are specified using the date pickers
// internationalization API
DatePicker.DatePickerI18n singleFormatI18n = new DatePicker.DatePickerI18n();
singleFormatI18n.setDateFormat("yyyy-MM-dd");

DatePicker singleFormatDatePicker = new DatePicker("Select a date:");
singleFormatDatePicker.setI18n(singleFormatI18n);

// Setup date picker with a primary format and additional parsing
// formats
// Date is always displayed using the primary format `yyyy-MM-dd`
// When parsing user input, the date picker first attempts to match the
// input with the primary format `yyyy-MM-dd`, then `MM/dd/yyyy`, and
// finally `dd.MM.yyyy`
DatePicker.DatePickerI18n multiFormatI18n = new DatePicker.DatePickerI18n();
multiFormatI18n.setDateFormats("yyyy-MM-dd", "MM/dd/yyyy",
        "dd.MM.yyyy");

DatePicker multiFormatDatePicker = new DatePicker("Select a date:");
multiFormatDatePicker.setI18n(multiFormatI18n);
```

A custom date format pattern is a string that consists of specific symbols that specify how and where a part of the date (day, month, or year) should be displayed. The following symbols are recognized as parts of the date in a pattern:

|        |                                        |
| ------ | -------------------------------------- |
| `d`    | Day of the month, as one or two digits |
| `dd`   | Day of the month, padded to two digits |
| `M`    | Month, as one or two digits            |
| `MM`   | Month, padded to two digits            |
| `yy`   | Year, using two digits                 |
| `yyyy` | Year, using four digits                |

<!-- vale Vaadin.Terms = NO -->

Other characters, such as separators (for example `/`, `.`, `-`) or spaces may be used in a pattern.

<!-- vale Vaadin.Terms = YES -->

Examples:

| Pattern      | Example value | Description                          |
| ------------ | ------------- | ------------------------------------ |
| `M/d/yyyy`   | `8/26/2021`   | United States date format            |
| `yyyy-MM-dd` | `2021-08-26`  | ISO 8601 date format                 |
| `d. M. yyyy` | `26. 8. 2021` | Croatian date format using spaces    |
| `M/d`        | `8/26`        | Date format using only day and month |

Custom date patterns take precedence over the configured locale. When using both at the same time, the custom date pattern is used to display and parse dates.

### <a id="two-digit-year-formats"></a>Two-Digit Year Formats

Date Picker supports date formats with 2-digit years (such as `dd.MM.yy`). When parsing a 2-digit year, the component must decide which century to parse the year as. For example, a year entered as `62` could be interpreted either as 62 AD, 1962, or 2062.

<!-- vale Vaadin.RangeFormat = NO -->

Two-digit years are interpreted as being within a 100-year range whose midpoint is called the *reference date* (since V23.3). For example, a reference date of 2000-01-01 means that the date entered is interpreted as being within the range 1950-01-01 – 2049-12-31, and the year `50` is parsed to 1950 while `49` is parsed to 2049.

<!-- vale Vaadin.RangeFormat = YES -->

The reference date defaults to the current date, but you can set it to a different date with the [internationalization API](#i18n):

```java
DatePicker.DatePickerI18n i18n = new DatePicker.DatePickerI18n();
i18n.setDateFormat("yy-MM-dd");
i18n.setReferenceDate(LocalDate.of(1980, 2, 2));
datePicker.setI18n(i18n);
```

```typescript
datePicker.i18n = {
  ...this.datePicker.i18n,
  referenceDate: '1980-02-02'
};
```

When using a display format with a 4-digit year, years prior to 1000 are displayed padded with leading zeroes to avoid ambiguity.

> **Note: Display formats with 2-digit years should only be used with 2-digit parsing formats**
>
> Using a display format with 2-digit years (`yy`) together with parsing formats with 4-digit years (`yyyy`) can cause unexpected behavior, and should be avoided.

### <a id="using-custom-functions-web-component-only"></a>Using Custom Functions (Web Component Only)

When using the web component standalone, custom functions can be configured to display and parse the date.

`date-picker-custom-functions.ts`

```typescript
protected override firstUpdated() {
  const formatDateIso8601 = (dateParts: DatePickerDate): string => {
    const { year, month, day } = dateParts;
    const date = new Date(year, month, day);

    return dateFnsFormat(date, 'yyyy-MM-dd');
  };

  const parseDateIso8601 = (inputValue: string): DatePickerDate => {
    const date = dateFnsParse(inputValue, 'yyyy-MM-dd', new Date());

    return { year: date.getFullYear(), month: date.getMonth(), day: date.getDate() };
  };

  this.datePicker.i18n = {
    ...this.datePicker.i18n,
    formatDate: formatDateIso8601,
    parseDate: parseDateIso8601,
  };
}
```

> **Note: Third-party library**
>
> The previous example uses the third-party library [date-fns](https://date-fns.org/) to implement the custom formatting and parsing functions. It needs to be added as a separate dependency to the project.

## <a id="validation"></a>Validation

### <a id="min-and-max-value"></a>Min and Max Value

The valid input range of Date Picker can be restricted by defining `min` and `max` values. Dates before the `min` and after the `max` are disabled in the overlay. A helper text can be used to inform the user about the accepted range.

**TypeScript** — `date-picker-min-max.ts`

```html
<vaadin-date-picker
  label="Appointment date"
  helper-text="Must be within 60 days from today"
  .min="${formatISO(this.minDate, { representation: 'date' })}"
  .max="${formatISO(this.maxDate, { representation: 'date' })}"
  .errorMessage="${this.errorMessage}"
  @change="${({ target }: DatePickerChangeEvent) => {
    const date = dateFnsParse(target.value ?? '', 'yyyy-MM-dd', new Date());
    if (isBefore(date, this.minDate)) {
      this.errorMessage = 'Too early, choose another date';
    } else if (isAfter(date, this.maxDate)) {
      this.errorMessage = 'Too late, choose another date';
    } else {
      this.errorMessage = '';
    }
  }}"
></vaadin-date-picker>
```

**Java** — `DatePickerMinMax.java`

```java
LocalDate now = LocalDate.now(ZoneId.systemDefault());

datePicker.setMin(now);
datePicker.setMax(now.plusDays(60));
datePicker.setHelperText("Must be within 60 days from today");
datePicker.addValueChangeListener(event -> {
    LocalDate value = datePicker.getValue();
    String errorMessage = null;
    if (value != null) {
        if (value.compareTo(datePicker.getMin()) < 0) {
            errorMessage = "Too early, choose another date";
        } else if (value.compareTo(datePicker.getMax()) > 0) {
            errorMessage = "Too late, choose another date";
        }
    }
    datePicker.setErrorMessage(errorMessage);
});
```

### <a id="custom-validation"></a>Custom Validation

Date Picker supports custom validation, such as limiting the options to Monday through Friday.

**TypeScript** — `date-picker-custom-validation.ts`

```typescript
private binder = new Binder(this, AppointmentModel);

protected override firstUpdated() {
  this.binder.for(this.binder.model.startDate).addValidator({
    message: 'Select a weekday',
    validate: (startDate: string) => {
      const date = new Date(startDate);
      const isWeekday = date.getDay() >= 1 && date.getDay() <= 5;
      return isWeekday;
    },
  });
}

protected override render() {
  return html`
    <vaadin-date-picker
      label="Meeting date"
      helper-text="Mondays – Fridays only"
      ${field(this.binder.model.startDate)}
    ></vaadin-date-picker>
  `;
}
```

**Java** — `DatePickerCustomValidation.java`

```java
DatePicker datePicker = new DatePicker("Meeting date");
datePicker.setHelperText("Mondays – Fridays only");

Binder<Appointment> binder = new Binder<>(Appointment.class);
binder.forField(datePicker).withValidator(localDate -> {
    int dayOfWeek = localDate.getDayOfWeek().getValue();
    boolean validWeekDay = dayOfWeek >= 1 && dayOfWeek <= 5;
    return validWeekDay;
}, "Select a weekday").bind(Appointment::getStartDate,
        Appointment::setStartDate);
```

## <a id="week-numbers"></a>Week Numbers

<!-- vale Vaadin.Abbr = NO -->

Week numbers ([ISO-8601](https://www.iso.org/iso-8601-date-and-time-format.html)) can be enabled in the calendar overlay. This works only when the first day of the week is set to Monday.

<!-- vale Vaadin.Abbr = YES -->

**TypeScript** — `date-picker-week-numbers.ts`

```typescript
protected override firstUpdated() {
  this.datePicker.i18n = {
    ...this.datePicker.i18n,
    firstDayOfWeek: 1,
  };
}

protected override render() {
  return html`
    <vaadin-date-picker label="Vacation start date" show-week-numbers></vaadin-date-picker>
  `;
}
```

**Java** — `DatePickerWeekNumbers.java`

```java
datePicker.setWeekNumbersVisible(true);
datePicker
        .setI18n(new DatePicker.DatePickerI18n().setFirstDayOfWeek(1));
```

## <a id="initial-position"></a>Initial Position

Date Picker’s initial position parameter defines which date is focused in the calendar overlay when the overlay is opened. The default initial position is the selected or current date.

Use this feature to minimize the need for unnecessary navigation and/or scrolling when the user’s input is expected to be within a certain time.

**TypeScript** — `date-picker-initial-position.ts`

```html
<vaadin-date-picker
  label="Q4 deadline"
  .initialPosition="${formatISO(this.lastDayOfTheYear, { representation: 'date' })}"
></vaadin-date-picker>
```

**Java** — `DatePickerInitialPosition.java`

```java
LocalDate lastDayOfYear = LocalDate.now(ZoneId.systemDefault())
        .with(TemporalAdjusters.lastDayOfYear());

datePicker.setInitialPosition(lastDayOfYear);
```

## <a id="auto-open"></a>Auto Open

The overlay automatically opens when the field is focused with a click or a tap, and when typing a value in the input. This can be prevented, to have the overlay only open when the toggle button or the up/down arrow keys are pressed. The behavior isn’t affected on touch devices.

**TypeScript** — `date-picker-auto-open.ts`

```html
<vaadin-date-picker label="Start date" auto-open-disabled></vaadin-date-picker>
```

**Java** — `DatePickerAutoOpen.java`

```java
datePicker.setAutoOpen(false);
```

## <a id="i18n"></a>Internationalization (i18n)

Date Picker allows localizing texts and labels, such as month names and button labels.

**TypeScript** — `date-picker-internationalization.ts`

```typescript
@query('vaadin-date-picker')
private datePicker!: DatePicker;

protected override firstUpdated() {
  this.datePicker.i18n = {
    ...this.datePicker.i18n,
    monthNames: [
      'Januar',
      'Februar',
      'März',
      'April',
      'Mai',
      'Juni',
      'Juli',
      'August',
      'September',
      'Oktober',
      'November',
      'Dezember',
    ],
    weekdays: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'],
    weekdaysShort: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
    week: 'Woche',
    today: 'Heute',
    cancel: 'Abbrechen',
  };
}

protected override render() {
  return html`<vaadin-date-picker label="Sitzungsdatum"></vaadin-date-picker>`;
}
```

**Java** — `DatePickerInternationalization.java`

```java
DatePicker.DatePickerI18n germanI18n = new DatePicker.DatePickerI18n();
germanI18n.setMonthNames(List.of("Januar", "Februar", "März", "April",
        "Mai", "Juni", "Juli", "August", "September", "Oktober",
        "November", "Dezember"));
germanI18n.setWeekdays(List.of("Sonntag", "Montag", "Dienstag",
        "Mittwoch", "Donnerstag", "Freitag", "Samstag"));
germanI18n.setWeekdaysShort(
        List.of("So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"));
germanI18n.setWeek("Woche");
germanI18n.setToday("Heute");
germanI18n.setCancel("Abbrechen");

datePicker.setI18n(germanI18n);
```

See also how to [configure a custom date format](#date-format).

## <a id="usage-patterns"></a>Usage Patterns

### <a id="date-range"></a>Date Range

You can create a date range picker using two Date Pickers.

**TypeScript** — `date-picker-date-range.ts`

```html
<vaadin-horizontal-layout theme="spacing">
  <vaadin-date-picker
    label="Departure date"
    .max="${this.returnDate}"
    @value-changed="${(event: DatePickerValueChangedEvent) => {
      this.departureDate = event.detail.value;
    }}"
  ></vaadin-date-picker>
  <vaadin-date-picker
    label="Return date"
    .min="${this.departureDate}"
    @value-changed="${(event: DatePickerValueChangedEvent) => {
      this.returnDate = event.detail.value;
    }}"
  ></vaadin-date-picker>
</vaadin-horizontal-layout>
```

**Java** — `DatePickerDateRange.java`

```java
DatePicker departureDate = new DatePicker("Departure date");
DatePicker returnDate = new DatePicker("Return date");
departureDate
        .addValueChangeListener(e -> returnDate.setMin(e.getValue()));
returnDate.addValueChangeListener(
        e -> departureDate.setMax(e.getValue()));
```

To disable the days before the start date in the end date picker, you need to handle the selection in the start date picker and change the range in the end date picker.

## <a id="best-practices"></a>Best Practices

### <a id="picking-vs-typing"></a>Picking vs Typing

The calendar overlay is useful when the users need to choose a day that’s close to the current date or when information such as day of the week, week number, and valid dates, and so on, can aid in choosing the best option.

For days well into the past or future, and for known dates such as birthdays, typing the date in the input field can be a faster and easier approach. Because of this, it’s important to verify that the user can enter dates according to their locale.

Instead of a Date Picker, you can use individual input fields for day, month, and year, to improve usability on small touch devices.

**TypeScript** — `date-picker-individual-input-fields.ts`

```typescript
@state()
selectedYear?: number;

@state()
selectedMonth?: string;

@state()
selectedDay?: number;

@state()
selectableDays: number[] = [];

private handleYearChange(e: ComboBoxSelectedItemChangedEvent<number>) {
  this.selectedYear = e.detail.value!;
  this.selectedMonth = undefined;
  this.selectedDay = undefined;
  this.selectableDays = [];
}

private handleMonthChange(e: ComboBoxSelectedItemChangedEvent<string>) {
  this.selectedMonth = e.detail.value!;
  this.selectedDay = undefined;

  if (!this.selectedYear || !this.selectedMonth) {
    this.selectableDays = [];
    return;
  }

  const startOfMonth = new Date(this.selectedYear, this.months.indexOf(this.selectedMonth), 1);
  const lengthOfMonth = getDaysInMonth(startOfMonth);

  this.selectableDays = Array.from({ length: lengthOfMonth }, (_, k) => k + 1);
}

private handleDayChange(e: ComboBoxSelectedItemChangedEvent<number>) {
  this.selectedDay = e.detail.value!;
}

protected override render() {
  return html`
    <vaadin-horizontal-layout theme="spacing">
      <vaadin-combo-box
        label="Year"
        style="width: 6em;"
        .items="${this.years}"
        .selectedItem="${this.selectedYear}"
        @selected-item-changed="${this.handleYearChange}"
      ></vaadin-combo-box>
      <vaadin-combo-box
        label="Month"
        style="width: 9em;"
        .items="${this.months}"
        .selectedItem="${this.selectedMonth}"
        .disabled="${!this.selectedYear}"
        @selected-item-changed="${this.handleMonthChange}"
      ></vaadin-combo-box>
      <vaadin-combo-box
        label="Day"
        style="width: 5em;"
        .items="${this.selectableDays}"
        .selectedItem="${this.selectedDay}"
        .disabled="${!this.selectedYear || !this.selectedMonth}"
        @selected-item-changed="${this.handleDayChange}"
      ></vaadin-combo-box>
    </vaadin-horizontal-layout>
  `;
}
```

**Java** — `DatePickerIndividualInputFields.java`

```java
public DatePickerIndividualInputFields() {
    LocalDate now = LocalDate.now(ZoneId.systemDefault());

    List<Integer> selectableYears = IntStream
            .range(now.getYear() - 99, now.getYear() + 1).boxed()
            .collect(Collectors.toList());

    yearPicker = new ComboBox<>("Year", selectableYears);
    yearPicker.setWidth(6, Unit.EM);
    yearPicker.addValueChangeListener(e -> {
        updateMonthPicker();
        updateDayPicker();
    });

    monthPicker = new ComboBox<>("Month", Month.values());
    monthPicker.setItemLabelGenerator(
            m -> m.getDisplayName(TextStyle.FULL, Locale.getDefault()));
    monthPicker.setWidth(9, Unit.EM);
    monthPicker.addValueChangeListener(e -> {
        updateDayPicker();
    });
    monthPicker.setEnabled(false);

    dayPicker = new ComboBox<>("Day");
    dayPicker.setWidth(5, Unit.EM);
    dayPicker.setEnabled(false);

    add(new HorizontalLayout(yearPicker, monthPicker, dayPicker));
}

private void updateMonthPicker() {
    if (yearPicker.getValue() == null) {
        monthPicker.setValue(null);
        monthPicker.setEnabled(false);
        return;
    }

    monthPicker.setValue(null);
    monthPicker.setEnabled(true);
}

private void updateDayPicker() {
    if (yearPicker.getValue() == null || monthPicker.getValue() == null) {
        dayPicker.setValue(null);
        dayPicker.setEnabled(false);
        return;
    }

    dayPicker.setValue(null);
    dayPicker.setEnabled(true);

    LocalDate startOfMonth = LocalDate.of(yearPicker.getValue(),
            monthPicker.getValue(), 1);
    int lengthOfMonth = startOfMonth.lengthOfMonth();

    dayPicker.setItems(IntStream.range(1, lengthOfMonth + 1).boxed()
            .collect(Collectors.toList()));
}
```

> **Note: Not production-ready**
>
> The previous example is only a prototype implementation to demonstrate the idea, and isn’t ready for production use.

### <a id="show-the-date-format"></a>Show the Date Format

<!-- vale Google.DateFormat = NO --><!-- vale Microsoft.DateOrder = NO -->

Use a placeholder or helper to show how the input should be formatted. For example, "12/6/2020" represents different dates for Americans and Europeans.

<!-- vale Microsoft.DateOrder = YES --><!-- vale Google.DateFormat = YES -->

**TypeScript** — `date-picker-date-format-indicator.ts`

```html
<vaadin-date-picker
  label="Start date"
  placeholder="DD/MM/YYYY"
  helper-text="Format: DD/MM/YYYY"
></vaadin-date-picker>
```

**Java** — `DatePickerDateFormatIndicator.java`

```java
datePicker.setPlaceholder("DD.MM.YYYY");
datePicker.setHelperText("Format: DD.MM.YYYY");
```

Helpers are preferable to placeholders, as they are always visible. Fields with placeholders are also less noticeable than empty fields, so they are susceptible to being skipped. Use placeholders when space is limited, for example when Date Picker is used as a filter in a data grid header.

## <a id="related-components"></a>Related Components

| Component                                                                      | Usage recommendations                                  |
| ------------------------------------------------------------------------------ | ------------------------------------------------------ |
| [Time Picker](https://vaadin.com/docs/v23/components/time-picker.md)           | Input field for entering or selecting a specific time. |
| [Date Time Picker](https://vaadin.com/docs/v23/components/date-time-picker.md) | Input field for selecting both a date and a time.      |

`26595CB7-1A81-4EE1-B94C-948E889C3027`
