react-modular-datepicker

Availability & Booking Calendar

Build appointment booking and availability calendars with custom modifier styles, disabled dates, and month change loading indicators.

Availability & Booking Calendar

Create availability calendars for appointments, hotel bookings, or resource scheduling using disabled dates, custom modifier classes, and async month navigation callbacks.

Live Preview: Availability & Booking Calendar
SUN
MON
TUE
WED
THU
FRI
SAT

Code Example

import { useState } from 'react';
import { Calendar } from 'react-modular-datepicker';
import 'react-modular-datepicker/dist/index.css';

const getRandomDatesInMonth = (year: number, month: number) => {
  const totalDaysInMonth = new Date(year, month + 1, 0).getDate();
  const count = Math.floor(Math.random() * 6) + 5;
  const randomDayNumbers = new Set<number>();

  while (randomDayNumbers.size < Math.min(count, totalDaysInMonth)) {
    randomDayNumbers.add(Math.floor(Math.random() * totalDaysInMonth) + 1);
  }

  return Array.from(randomDayNumbers).map((day) => new Date(year, month, day));
};

export function AvailabilityExample() {
  const today = new Date();
  const year = today.getFullYear();
  const month = today.getMonth();

  const [bookedDays, setBookedDays] = useState(
    getRandomDatesInMonth(year, month)
  );
  const [selectedDate, setSelectedDate] = useState<Date | null>(
    new Date(year, month, 8)
  );
  const [isLoading, setIsLoading] = useState(false);

  const handleMonthChange = (dates: Date[]) => {
    setIsLoading(true);
    setTimeout(() => {
      const firstDate = dates[0] || new Date();
      const targetYear = firstDate.getFullYear();
      const targetMonth = firstDate.getMonth();

      setBookedDays(getRandomDatesInMonth(targetYear, targetMonth));
      setIsLoading(false);
    }, 700);
  };

  return (
    <div className="relative inline-block">
      <Calendar
        selected={selectedDate || undefined}
        onChange={(d) => setSelectedDate(d as Date)}
        onMonthChange={handleMonthChange}
        disabledDates={bookedDays}
        classNames={{
          day: {
            disabled: 'text-red-400 line-through cursor-not-allowed',
          },
        }}
      />

      {isLoading && (
        <div className="absolute inset-0 bg-white/80 backdrop-blur-xs flex flex-col items-center justify-center rounded-lg z-20">
          <div className="w-7 h-7 border-3 border-[#c5a059] border-t-transparent rounded-full animate-spin mb-2" />
          <span className="text-xs font-semibold text-gray-700">Fetching availabilities...</span>
        </div>
      )}
    </div>
  );
}

On this page