Gym or studio weekly schedule with day tabs, class list per day (instructor avatar, duration, spots remaining, difficulty), and quick reserve action. Tap a day to see its classes, tap a class to book a spot.
A gym or studio weekly schedule card that renders a horizontally scrollable row of day tabs above a per-day class list, where each class row shows a time chip, name, difficulty badge, instructor avatar, duration, spots-remaining count, and a reserve button, capped by a fixed difficulty legend footer.
Pro blocks install through the shadcn CLI with your license key and ship their full source. Docs and live previews stay open to everyone, so you can read every block's details first.
Swap YOUR_EMAIL and YOUR_KEY for the email and license key on your account. Find your license key on your account page.
Radix flavor
npx shadcn add "https://ui.beste.co/r/booking21?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://ui.beste.co/r-base/booking21?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/booking21.tsx, plus the avatar, badge, button, and tabs shadcn/ui primitives it uses for the instructor avatars, difficulty and eyebrow badges, reserve buttons, and the day-tab switcher.
The installed file exports booking21Demo alongside the block: the exact props behind the preview above. Spread it to get a working weekly schedule in one line.
import { Booking21, booking21Demo } from "@/components/beste/block/booking21";
export default function SchedulePage() {
return <Booking21 {...booking21Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Booking21 } from "@/components/beste/block/booking21";
export default function SchedulePage() {
return (
<Booking21
badge={{ label: "Group classes", variant: "secondary" }}
heading="This week's <strong>classes</strong>"
description="Filter by day to see instructors, duration, and spots left."
days={[
{
date: "Mon, Jul 13",
classes: [
{
id: "mon-yoga",
name: "Sunrise Yoga",
instructor: "Maya Patel",
instructorAvatar: "https://example.com/maya.jpg",
startTime: "07:00",
duration: "45 min",
capacity: 18,
reserved: 12,
difficulty: "easy",
},
],
},
]}
labels={{
spotsSuffix: "spots left",
fullLabel: "Full",
reserveButton: "Reserve",
reservedButton: "Reserved",
easyLabel: "Easy",
moderateLabel: "Moderate",
intenseLabel: "Intense",
todayLabel: "Today",
emptyMessage: "No classes scheduled",
}}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
badge | { label: string; variant?: "default" | "secondary" | "outline" } | – | Optional eyebrow badge above the heading |
heading | string | – | Section heading, supports inline <strong> |
description | string | – | Paragraph rendered below the heading |
days | ScheduleDay[] | [] | One tab and class list per day |
labels | Booking21Labels | {} | UI copy for buttons, difficulty names, and empty state |
className | string | – | Extra classes for the outer <section> |
type ClassDifficulty = "easy" | "moderate" | "intense";
type ScheduleClass = {
id: string;
name: string;
instructor: string;
instructorAvatar: string;
startTime: string;
duration: string;
capacity: number;
reserved: number;
difficulty: ClassDifficulty;
};
type ScheduleDay = {
date: string;
classes: ScheduleClass[];
};
type Booking21Labels = {
durationLabel?: string;
spotsSuffix?: string;
fullLabel?: string;
reserveButton?: string;
reservedButton?: string;
easyLabel?: string;
moderateLabel?: string;
intenseLabel?: string;
todayLabel?: string;
emptyMessage?: string;
};useState (activeDay starts at "0", reserved is an empty Set) and are never lifted out, so there is no controlled-value or onChange prop to observe which day is open or which classes a user booked.toggleReserve flips the class id in the local Set, and the only outward signal is a console.log("Class reservation:", ...) fired when a class is newly reserved (not on un-reserve), so persisting a booking is left to the integrator.reserved >= capacity and not already locally reserved; full rows dim to opacity-60, show the fullLabel in place of the spots count, and their reserve button is disabled.capacity - reserved; when that count is 3 or fewer the number turns rose-colored to flag low availability.date string on its comma: the first tab shows todayLabel (index 0 is always assumed to be today), every other tab shows the weekday half, and the second line shows the "Mon Day" half, so a date without a ", " separator renders a blank second line.heading is injected via dangerouslySetInnerHTML, so inline <strong> is styled as bold primary text; pass trusted markup only.classes array render an activity-icon empty state with emptyMessage instead of a list, and the header block (badge, heading, description) is omitted entirely when all three are absent.easyLabel, moderateLabel, and intenseLabel copy.booking3
Upcoming and past appointments list grouped by date, with service name, provider avatar, time, duration, status badges (confirmed/pending/cancelled), and reschedule/cancel action buttons.
booking19
Stepper-based booking flow with numbered progress dots, animated progress bar, and four steps: service selection, schedule, contact details, and review. Back/Next navigation, per-step validation, and a live summary.
booking14
Tours and experiences booking card with hero image overlay, rating, duration, difficulty badge, date picker, traveler counter, language selector, highlights list, and reserve CTA. Perfect for guided tours, classes, and local experiences.
booking8
Flight search results with airline logos, departure/arrival times, duration, stops, class selector (economy/business/first), baggage info, and price comparison. Airline ticket booking flow.
booking17
Ride-share style booking with pickup/dropoff address inputs, schedule now/later toggle, ride class selector (economy/comfort/xl) with ETA and fare, payment method row, and confirm ride CTA. Perfect for taxi, transfer, and shuttle apps.
booking10
Coworking meeting room reservation with room cards showing capacity, equipment icons, hourly pricing, time range selector, attendee count, and total cost. Office and coworking space booking flow.