Booking
A date and time slot picker with a month calendar view, day slots view, and three flexible layout modes. Pass your availability data and receive a typed selection — no state management required.
Overview
Introduction
BookingScheduler renders a calendar for date selection and a time slot list for hour picking. Switch between three modes — side-by-side, calendar only, or slots only — and toggle price and duration display with boolean props.
Setup
Getting started
Install the package from npm:
npm install @widgetkit/booking-react
# or
pnpm add @widgetkit/booking-reactImport the component and its stylesheet:
import { BookingScheduler } from "@widgetkit/booking-react";
import "@widgetkit/booking-react/styles.css";Pass availability — the only required prop. Use onSelect to receive the user's selection:
import { useState } from "react";
import { BookingScheduler } from "@widgetkit/booking-react";
import "@widgetkit/booking-react/styles.css";
import type { AvailabilityDay, BookingSelection } from "@widgetkit/booking-react";
const availability: AvailabilityDay[] = [
{
date: "2024-06-10",
available: true,
price: "€40",
slots: [
{ time: "09:00", available: true, duration: 60, price: "€40" },
{ time: "10:00", available: true, duration: 60, price: "€40" },
{ time: "11:00", available: false, duration: 60 },
{ time: "14:00", available: true, duration: 90, price: "€55" },
],
},
];
export function App() {
const [selection, setSelection] = useState<BookingSelection | null>(null);
return (
<BookingScheduler
availability={availability}
onSelect={setSelection}
/>
);
}Example
Month + Day
The default mode. The calendar and time slot list are shown side by side — the user first picks a date, then a time slot. On narrow viewports they stack vertically.
import { useState } from "react";
import { BookingScheduler } from "@widgetkit/booking-react";
import "@widgetkit/booking-react/styles.css";
import type { AvailabilityDay, BookingSelection } from "@widgetkit/booking-react";
// availability comes from your API
const availability: AvailabilityDay[] = [...];
export function App() {
const [selection, setSelection] = useState<BookingSelection | null>(null);
return (
<BookingScheduler
availability={availability}
onSelect={setSelection}
/>
);
}Example
Month only
Shows the calendar without any time slot step. Useful for date-only bookings, event registrations, or when you handle time selection in a separate step. onSelect fires with date and duration: 0 — no time field.
// Calendar-only — user picks a date, no time slot step
<BookingScheduler
availability={availability}
mode="month-only"
onSelect={(s) => console.log("Date:", s.date)}
/>Example
Day only
Shows only the time slot list for a specific day. Use this when the date is already known — for example inside a calendar cell, a resource detail panel, or a multi-step flow where the date was chosen in a previous step. Control which day is shown via the date prop.
// Time slots only — pass date to control which day is shown
<BookingScheduler
availability={availability}
mode="day-only"
date={new Date("2024-06-10")}
onSelect={(s) => console.log("Slot:", s.time, "·", s.duration, "min")}
/>Example
Generate slots
generateSlots is a utility exported from all three packages. Give it one or more time windows and a duration — it returns a list of AvailabilitySlot objects ready to drop into an AvailabilityDay. Use it server-side to build your availability array from your calendar or booking rules.
import { generateSlots } from "@widgetkit/booking-react";
// also available from "@widgetkit/booking-vue" or "@widgetkit/booking"
// Two availability windows: 09:00–12:00 and 13:00–17:00
// 60-minute slots, 30-minute intervals
const slots = generateSlots(
[
{ from: "09:00", to: "12:00" },
{ from: "13:00", to: "17:00" },
],
60, // duration in minutes
30, // interval (optional — defaults to duration)
);
// Each slot starts within a window and its full duration fits before the window ends.
// [
// { time: "09:00", available: true, duration: 60 },
// { time: "09:30", available: true, duration: 60 },
// { time: "10:00", available: true, duration: 60 },
// { time: "10:30", available: true, duration: 60 },
// { time: "11:00", available: true, duration: 60 },
// { time: "13:00", available: true, duration: 60 },
// ...
// ]Reference
Props
All props except availability are optional. Props marked * are required.
| Prop | Type | Default | Description |
|---|---|---|---|
| Data | |||
availability* | AvailabilityDay[] | — | Array of days defining which dates and time slots are bookable |
| Mode | |||
mode | "month-day" | "month-only" | "day-only" | "month-day" | Layout — calendar + slots side by side, calendar only, or slots only |
date | Date | — | Day to display in day-only mode |
initialMonth | Date | current month | Month shown on first render |
minDate | Date | — | Earliest selectable date |
maxDate | Date | — | Latest selectable date |
| Display | |||
showPrice | boolean | true | Show price label on day cells and slot rows |
showDuration | boolean | true | Show duration label inside slot rows |
| Callbacks | |||
onSelect | (selection: BookingSelection) => void | — | Called when a date (month-only) or time slot is confirmed |
Reference
TypeScript types
All types are exported from both @widgetkit/booking-react and @widgetkit/booking-vue. The @widgetkit/booking core package also exports them if you need them framework-agnostically.
import type {
AvailabilityDay,
AvailabilitySlot,
BookingMode,
BookingSelection,
TimeWindow,
} from "@widgetkit/booking-react";type BookingMode = "month-day" | "month-only" | "day-only";
interface AvailabilitySlot {
time: string; // "HH:mm"
available: boolean;
duration?: number; // minutes
price?: number | string;
}
interface AvailabilityDay {
date: string; // "YYYY-MM-DD"
available: boolean;
price?: number | string;
slots?: AvailabilitySlot[];
}
// Returned by onSelect / emitted by @select
interface BookingSelection {
date: string; // "YYYY-MM-DD" — always present
time?: string; // "HH:mm" — absent in month-only mode
duration: number; // minutes
}
// Used with generateSlots()
interface TimeWindow {
from: string; // "HH:mm"
to: string; // "HH:mm"
}