Multi-Timezone Booking Calendar for React and shadcn/ui

A shadcn/ui calendar component with multi-timezone slot booking, recurring weekly availability windows, and Month/Week/Day/Agenda views for React/Next.js projects.

Calendar Scheduler is a multi-view scheduling component that creates timezone-aware slot booking inside your React projects built with shadcn/ui.

It runs on date-fns and date-fns-tz for all date arithmetic, and draws on shadcn/ui’s component primitives for the rendered UI.

You can define weekly availability with weekday rules, and the component turns those windows into slot-based booking choices.

Booked entries close matching times on the calendar and keep the schedule readable for viewers in different time zones.

Features

📅 Multiple Calendar Views: Switches between Month, Week, Day, and Agenda display modes.

🗓️ Recurring Weekly Availability: Availability windows repeat by weekday name and apply across all future weeks.

⏱️ Configurable Slot Durations: Slot lengths run at 15, 30, or 60 minutes.

🚫 Booked Slot Tracking: The UI marks specific date-time slots as unavailable based on a passed-in array of booked entries.

🌍 Admin-to-Viewer Timezone Conversion: Admin availability windows convert automatically to each viewer’s local timezone on render.

How to Use It

1. Install the calendar component via shadcn CLI. This copies all calendar component files into src/components/calendar/ in your project.

npx shadcn@latest add https://raw.githubusercontent.com/gluer-space/calendar/main/public/r/registry.json

2. Or install it manually as follows:

Your project must have Tailwind CSS configured, the @/components/ui/ path alias pointing to your shadcn/ui components, and lucide-react installed as a package.

npm install date-fns date-fns-tz lucide-react
npx shadcn@latest add button select command popover tooltip
cp -r src/components/calendar your-project/src/components/

3. Import CalendarScheduler from the component directory and pass in your availability config, booked slots, slot duration, and callback handlers:

import { CalendarScheduler } from "@/components/calendar";
export default function BookingPage() {
  const [selectedDate, setSelectedDate] = useState<Date | null>(null);
  function handleBooking(date: Date, time: string) {
    console.log("Booked:", date, time);
    // Call your booking API here
  }
  return (
    <CalendarScheduler
      availability={[
        { day: "monday", startTime: "09:00", endTime: "17:00", enabled: true },
        { day: "tuesday", startTime: "10:00", endTime: "16:00", enabled: true },
        { day: "friday", startTime: "09:00", endTime: "13:00", enabled: true },
      ]}
      bookedSlots={[
        { date: "2024-04-10", time: "11:00" },
        { date: "2024-04-12", time: "14:30" },
      ]}
      slotDuration={30}
      adminTimeZone="America/Chicago"
      defaultViewerTimeZone="Europe/London"
      selectedDate={selectedDate ?? undefined}
      onDateSelect={(date) => setSelectedDate(date)}
      onSlotSelect={(date, time) => handleBooking(date, time)}
    />
  );
}

4. Available component props

PropTypeRequiredDescription
availabilityAvailability[]YesArray of weekly availability windows. Each entry defines a weekday, start time, end time, and enabled flag.
bookedSlotsBookedSlot[]NoArray of date-time pairs that mark specific slots as already taken.
slotDurationnumberYesLength of each bookable slot in minutes. Accepts 15, 30, or 60.
adminTimeZonestringNoIANA timezone string for the admin’s availability definition (e.g., "America/New_York").
defaultViewerTimeZonestringNoIANA timezone string for the viewer’s display. Falls back to browser locale if omitted.
selectedDateDateNoPre-selected date shown on initial render.
onDateSelect(date: Date) => voidYesFires when the user selects a date.
onSlotSelect(date: Date, time: string) => voidYesFires when the user clicks an open time slot.

5. Available components:

All components live in src/components/calendar/:

ComponentFileDescription
CalendarSchedulercalendar.tsxMain entry point. Renders all views and manages state.
MonthViewmonth-view.tsxFull month grid with day-level slot indicators.
WeekViewweek-view.tsxWeekly column layout with hourly time slots.
DayViewday-view.tsxSingle-day detail view.
AgendaViewagenda-view.tsxChronological list view of upcoming available slots.

Related Resources

  • shadcn/ui: Copy-paste React component library built on Radix UI and Tailwind CSS.
  • date-fns: Modular JavaScript date utility library that handles all date arithmetic in this component.
  • date-fns-tz: Timezone-aware extension for date-fns built on the IANA timezone database.

FAQs

Q: What happens if adminTimeZone is not set?
A: The component treats availability times as-is with no conversion. Set adminTimeZone to an IANA string like "America/New_York" whenever your admin and viewers are in different time zones.

Q: Can I change slot duration at runtime?
A: Yes. The slotDuration prop controls slot length at the component level. Update the value passed to the prop and the component re-renders with the new slot grid.

Q: Does the component handle recurring blocked times, or only individual booked slots?
A: The bookedSlots prop marks specific date-time pairs as taken. For recurring blocks, generate the blocked entries from your backend and pass the full array on each render.

Q: Does this work with Next.js App Router?
A: Yes. Add "use client" at the top of any file that renders CalendarScheduler.

Rahul Rajput

Rahul Rajput

Leave a Reply

Your email address will not be published. Required fields are marked *