Weekly Class Schedule

Group classes

This week's classes

Drop in or reserve your spot. Filter by day to see instructors, duration, and how many spots are left.

07:00

Sunrise Yoga

Easy
Maya Patel
45 min6 spots left
12:30

HIIT Burn

Intense
Marcus Lee
30 minFull
18:00

Pilates Core

Moderate
Sofia Ruiz
60 min7 spots left
EasyModerateIntense
About this block

Weekly Class SchedulePRO

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.

Booking21: Weekly Class Schedule

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.

Upgrade to Pro

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.

Upgrade Now

Installation

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

bash
npx shadcn add "https://ui.beste.co/r/booking21?email=YOUR_EMAIL&license_key=YOUR_KEY"

Base UI flavor

bash
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.

Quick start

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.

tsx
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:

tsx
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",
      }}
    />
  );
}

Props

PropTypeDefaultDescription
badge{ label: string; variant?: "default" | "secondary" | "outline" }Optional eyebrow badge above the heading
headingstringSection heading, supports inline <strong>
descriptionstringParagraph rendered below the heading
daysScheduleDay[][]One tab and class list per day
labelsBooking21Labels{}UI copy for buttons, difficulty names, and empty state
classNamestringExtra classes for the outer <section>
ts
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;
};

Behavior notes

  • The active day and the set of reserved class ids live in internal 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.
  • Reserving is a pure client toggle: 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.
  • A class is treated as full when 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.
  • Remaining spots are computed as capacity - reserved; when that count is 3 or fewer the number turns rose-colored to flag low availability.
  • Day tab labels are derived by splitting each 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.
  • Days with an empty 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.
  • The day-tab strip scrolls horizontally with its scrollbar hidden, and the difficulty legend footer is static and decorative, driven only by the easyLabel, moderateLabel, and intenseLabel copy.

More Booking blocks

View all Booking
PRO

booking3

Appointment List

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.

PRO

booking19

Multi-step Booking Wizard

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.

PRO

booking14

Activity Experience Booking

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.

PRO

booking10

Meeting Room Booking

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.

PRO

booking17

Ride Booking

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.

PRO

booking8

Flight Booking

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.