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.
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.
Date Format
The date picker can be configured to display dates and parse user input in a specific format.
Using Java Locales (Java Only)
By default, the date picker displays and parses dates using the user’s locale (reference). Alternatively, setting a specific locale ensures that all users consistently see the same format.
new tab
Locale finnishLocale = new Locale("fi", "FI");
DatePicker datePicker = new DatePicker("Select a date:");
datePicker.setLocale(finnishLocale);
The date format that is used based on the locale depends on the specific browser implementation and might not be reliable when expecting a specific pattern. For more fine-grained control over the date format see the next section.
Using Custom Date Formats (Java Only)
The date picker allows configuring 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 attempted first with the primary format, then by additional parsing formats in the order that they are provided.
new tab
// 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:
| Day of the month, as one or two digits |
| Day of the month, padded to two digits |
| Month, as one or two digits |
| Month, padded to two digits |
| Year, using two digits |
| Year, using four digits |
Other characters, such as separators (for example /
, .
, -
) or spaces may be used in a pattern.
Examples:
Pattern | Example value | Description |
---|---|---|
|
| United States date format |
|
| ISO 8601 date format |
|
| Croatian date format using spaces |
|
| Date format using only day and month |
Custom date patterns take precedence over the locale configured in the date picker. When using both at the same time, the custom date pattern is used for displaying and parsing dates.
Using Custom Functions (Web Component Only)
When using the web component standalone, or through Fusion, custom functions can be configured for displaying and parsing the date.
new tab
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() };
};
if (this.datePicker) {
this.datePicker.i18n = {
...this.datePicker.i18n,
formatDate: formatDateIso8601,
parseDate: parseDateIso8601,
};
}
}
Note | Third-party library The example above uses the third-party library date-fns to implement the custom formatting and parsing functions. It needs to be added as a separate dependency to the project. |
Validation
Min & 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.
new tab
<vaadin-date-picker
.min="${this.today}"
.max="${this.upperLimit}"
label="Appointment date"
helper-text="Must be within 60 days from today"
></vaadin-date-picker>
Custom Validation
Date Picker supports custom validation, such as limiting the options to Monday through Friday.
new tab
private binder = new Binder(this, AppointmentModel);
firstUpdated() {
this.binder.for(this.binder.model.startDate).addValidator({
message: 'Please select a weekday',
validate: (startDate: string) => {
const date = new Date(startDate);
const isWeekday = date.getDay() >= 1 && date.getDay() <= 5;
return isWeekday;
},
});
}
render() {
return html`
<vaadin-date-picker
label="Meeting date"
helper-text="Mondays – Fridays only"
...="${field(this.binder.model.startDate)}"
></vaadin-date-picker>
`;
}
Week Numbers
Week numbers (ISO-8601) can be enabled in the calendar overlay. This works only when the first day of the week is set to Monday.
new tab
firstUpdated() {
if (this.datePicker) {
this.datePicker.i18n = {
...this.datePicker.i18n,
firstDayOfWeek: 1,
};
}
}
render() {
return html`
<vaadin-date-picker label="Vacation start date" show-week-numbers></vaadin-date-picker>
`;
}
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 minimise the need for unnecessary navigation and/or scrolling when the user’s input is expected to be within a certain time.
new tab
<vaadin-date-picker
label="Q4 deadline"
.initialPosition="${formatISO(this.lastDayOfTheYear, { representation: 'date' })}"
></vaadin-date-picker>
Auto Open
The overlay automatically opens when the field is focused. This can be prevented, to have the overlay only open when the toggle button or Up/Down arrow keys are pressed. Note, that the behavior is not affected on touch devices.
Usage Patterns
Date Range
You can create a date range picker using two Date Pickers.
new tab
<vaadin-horizontal-layout theme="spacing">
<vaadin-date-picker
label="Departure date"
@value-changed="${(e: DatePickerValueChangedEvent) =>
(this.departureDate = e.detail.value)}"
.max="${this.returnDate}"
></vaadin-date-picker>
<vaadin-date-picker
label="Return date"
@value-changed="${(e: DatePickerValueChangedEvent) => (this.returnDate = e.detail.value)}"
.min="${this.departureDate}"
></vaadin-date-picker>
</vaadin-horizontal-layout>
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.
Best Practises
Picking vs Typing
The calendar overlay is useful when the users need to choose a day that is close to the current date or when information such as day of the week, week number, and valid dates, etc. can aid in choosing the best option.
For days well in the past or future, and for known dates such as birthdays, typing the date in the input field can be the faster and easier approach. Because of this, it is 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.
new tab
@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!;
}
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>
`;
}
Note | Not production-ready The above example is only a prototype implementation to demonstrate the idea, and not ready for production use. |
Show the Date Format
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.
new tab
<vaadin-date-picker
label="Start date"
placeholder="DD/MM/YYYY"
helper-text="Format: DD/MM/YYYY"
></vaadin-date-picker>
Helpers are preferable to placeholders, as they are always visible. Fields with placeholders are also less noticeable than empty fields and 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.