How to Properly Handle Invalid Dates in Flatpickr with Manual Input
When using Flatpickr with the allowInput: true option enabled, users can manually type dates into the input field. While this improves flexibility and accessibility, it often causes issues when users enter non-existent or malformed dates (like 31/04/2026 or random text).
The Problem: Infinite Loops with parseDate
A common approach is using the parseDate callback along with a date library like Moment.js or Day.js. However, returning a fallback date (or triggering alert dialogs) inside parseDate often triggers an infinite loop or disrupts Flatpickr's internal redraw cycle. Because parseDate is called continuously during input evaluation and state synchronization, mutating the state or alerting inside it breaks the flow.
The Best Practice Solution: Use the onClose or onChange Hook
Instead of intercepting input inside parseDate to handle invalid validation states, use Flatpickr's onClose or custom blur listener to validate the input value once the user has finished typing.
Here is a clean and reliable implementation using pure JavaScript (or with Day.js / Moment.js if preferred):
const fp = flatpickr("#my-date-picker", {
altInput: true,
dateFormat: "Y-m-d",
altFormat: "d/m/Y",
allowInput: true,
// Custom parser to strictly validate format without triggering UI alerts
parseDate: (datestr, format) => {
// Use native parsing or a date library like moment/dayjs
const parts = datestr.split("/");
if (parts.length === 3) {
const day = parseInt(parts[0], 10);
const month = parseInt(parts[1], 10) - 1;
const year = parseInt(parts[2], 10);
const date = new Date(year, month, day);
// Check if the constructed date matches input numbers (handles 31st April edge case)
if (
date.getFullYear() === year &&
date.getMonth() === month &&
date.getDate() === day
) {
return date;
}
}
return undefined; // Returning undefined signals an invalid date to Flatpickr
},
onClose: (selectedDates, dateStr, instance) => {
const rawInput = instance.altInput ? instance.altInput.value : instance.input.value;
// If the input is not empty but no valid date was parsed
if (rawInput && selectedDates.length === 0) {
alert(`Unable to parse date: "${rawInput}". Resetting to today.`);
// Reset to today's date (or clear with instance.clear())
instance.setDate(new Date(), true);
}
}
});
Why This Approach Works
- Separation of Concerns:
parseDateremains a pure function. It accepts a string and returns either a validDateobject orundefinedwithout side effects or alerts. - Prevents Recursive Loops: Modifying the date inside
onClosetriggers an update only when the user leaves the input field, avoiding the infinite redraw loops caused by mid-keystroke date overrides. - Supports Alternate Inputs: When using
altInput: true, Flatpickr creates a hidden input and a visible surrogate input (instance.altInput). Accessinginstance.altInput.valueallows you to accurately check what the user actually typed.
Alternative: Resetting or Clearing on Invalid Input
If you prefer to clear the invalid text rather than defaulting to today's date, you can simply invoke instance.clear() inside the onClose hook:
onClose: (selectedDates, dateStr, instance) => {
const rawInput = instance.altInput ? instance.altInput.value : instance.input.value;
if (rawInput && selectedDates.length === 0) {
// User entered invalid text, wipe it out
instance.clear();
}
}
Conclusion
When handling manual input validation in Flatpickr, never execute UI-blocking logic (like alerts) or state resets directly inside parseDate. Let parseDate evaluate the validity silently and use the lifecycle hooks such as onClose to validate and sanitize user input smoothly.