← Back to Examples

⚡ Events & Callbacks

Event system and beforeDateSelectCallback for validation and control of date selections

Events Fired by the Component

the wrapper forwards these to LiveView as web_daterangepicker:change — see the floating panel

The date picker fires two custom events when a date or range is selected:

Event Names:
  • date-select - Primary event for date selection
  • change - Alias event (fires simultaneously with date-select)

Note: Both events bubble and are composed (cross Shadow DOM boundaries).

Event Detail Structure

// Single mode
{ formattedValue: "2025-11-17", date: Date object }

// Range mode
{
  formattedValue: "2025-11-17 - 2025-11-24",
  dateRange: { start: Date, end: Date },
  enabledDates: [Date, ...],  // 'allow' mode
  disabledDates: [Date, ...], // 'allow' mode
  dateRanges: [DateRange, ...],  // 'split' mode
  dates: [Date, ...],            // 'split' or 'individual' mode
  getEnabledDateCount: () => number,
  getTotalDays: () => number
}

Listening to Events

Select a date to see events...
Select a range to see events...
const picker = document.getElementById('my-picker');

picker.addEventListener('date-select', (event) => {
  console.log('Date selected:', event.detail);
  if (event.detail.date) {
    console.log('Selected date:', event.detail.date);      // single
  } else if (event.detail.dateRange) {
    console.log('Selected range:', event.detail.dateRange); // range
  }
});

beforeDateSelectCallback Callback - Validation & Control

The beforeDateSelectCallback callback is invoked BEFORE a selection is finalized. It allows you to validate, block, adjust, or clear selections based on your business logic.

Key Features:
  • Works for both single and range modes
  • Supports synchronous and asynchronous validation
  • Can block, adjust, restore, or clear selections
  • Shows loading overlay during async operations
  • Called AFTER local validation (disabled dates, min/max) passes

Return Value Actions

Action Description Use Case
'accept'Allow the selection as-isValidation passed
'adjust'Modify the selected date(s)Round to business days, snap to availability
'restore'Reject and revert to previous selectionValidation failed, keep previous state
'clear'Clear the selection entirelyHard reset on validation failure

Single Mode - beforeDateSelectCallback Examples

Try selecting Saturday or Sunday
Waiting for selection...
Only future dates allowed
Waiting for selection...
Weekends auto-adjust to Monday
Waiting for selection...
Simulates API call (500ms delay)
Waiting for selection...

Code Examples

// Block weekends
picker.beforeDateSelectCallback = ({ date }) => {
  const isWeekend = date.getDay() === 0 || date.getDay() === 6;
  return isWeekend
    ? { action: 'restore', message: 'Weekends not allowed' }
    : { action: 'accept' };
};

// Adjust to next business day
picker.beforeDateSelectCallback = ({ date }) => {
  const day = date.getDay();
  if (day === 0) { // Sunday → Monday
    const adjusted = new Date(date); adjusted.setDate(date.getDate() + 1);
    return { action: 'adjust', adjustedDate: adjusted, message: 'Adjusted to Monday' };
  }
  return { action: 'accept' };
};

// Async API validation
picker.beforeDateSelectCallback = async ({ date }) => {
  const { available } = await (await fetch('/api/check-date', {
    method: 'POST', body: JSON.stringify({ date: date.toISOString() })
  })).json();
  return available ? { action: 'accept' } : { action: 'restore', message: 'Date not available' };
};

Range Mode - beforeDateSelectCallback Examples

Requires at least 2 nights
Waiting for selection...
Cannot exceed 7 nights. Uses showInvalidRange: true to keep invalid range visible with error styling.
Waiting for selection...
End date must be Sunday (checkout day)
Waiting for selection...
Select more than 5 nights to see custom HTML error with alternatives. Uses showMessage(html) for full control.
Waiting for selection...

Code Examples

// Minimum nights requirement
picker.beforeDateSelectCallback = ({ range }) => {
  const nights = Math.floor((range.end - range.start) / (1000 * 60 * 60 * 24));
  return nights < 2
    ? { action: 'restore', message: 'Minimum 2 nights required' }
    : { action: 'accept' };
};

// Maximum nights with showInvalidRange
picker.beforeDateSelectCallback = ({ range }) => {
  const nights = Math.floor((range.end - range.start) / (1000 * 60 * 60 * 24));
  if (nights > 7) {
    picker.showMessage('Maximum 7 nights allowed', 'error');
    return { action: 'restore', showInvalidRange: true };
  }
  return { action: 'accept' };
};

// Handle custom-action from showMessage() buttons
picker.addEventListener('custom-action', (e) => {
  const { startDate, endDate } = e.detail.data;   // v2.0.0: data-* live under detail.data
  if (startDate && endDate) {
    picker.selectedRanges = [{ start: new Date(startDate), end: new Date(endDate) }];
    picker.hideMessage();
  }
});

beforeMonthChangedCallback - Performance Optimization

The beforeMonthChangedCallback is called BEFORE month navigation occurs. Use it to bulk-load metadata for all visible dates in ONE API call instead of calling getDateMetadataCallback 35-42 times per month.

Performance: 1 API call per month vs 35-42 calls with per-day callbacks
Navigate months to see bulk metadata loading in action. Check console for logs.
Navigate to a month to see bulk loading...
Try navigating more than 2 months into the future - navigation will be blocked
Try navigating to future months...
Anchor month at index 2 (top-right). Navigation loads all 6 months at once. Try navigating beyond ±3 months to see blocking.
Navigate to see unified loading...

Custom Month Headers

Customize the month header text (e.g., "January 2026") to display additional information like room availability, occupancy rates, or any business-specific data.

Two Approaches:
  • getMonthHeaderCallback - Callback to generate header on-the-fly
  • monthHeaders in beforeMonthChangedCallback result - Return headers with async-loaded data

Priority: monthHeaders > getMonthHeaderCallback > default format

Headers show working day counts (excludes weekends & holidays)
Headers will update as you navigate...
Headers loaded with pricing data
Navigate to load headers with data...
Cannot navigate more than 3 months ahead
Try navigating past 3 months...
Headers show occupancy rates with icons
Headers show occupancy percentage...

Code Examples

// Approach 1: getMonthHeaderCallback
picker.getMonthHeaderCallback = ({ month, monthName, year }) => {
  const rooms = roomAvailability[`${year}-${month.getMonth()}`] || 0;
  return `${monthName} ${year} (${rooms} rooms)`;
};

// Approach 2: monthHeaders from beforeMonthChangedCallback
picker.beforeMonthChangedCallback = async ({ year, month }) => {
  const data = await fetchAvailability(year, month);
  const monthHeaders = new Map();
  const monthKey = `${year}-${String(month).padStart(2, '0')}`;
  monthHeaders.set(monthKey, `${monthName} ${year} (${data.totalRooms} rooms)`);
  return { action: 'accept', metadata: data.dateMetadata, monthHeaders };
};
🔌 Server round-trip
0
The LiveView server saw 0 event(s) from the wrapper.
(pick a date anywhere on this page)