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.
A single-column coworking room reservation card: a header showing the space name, a location badge, and a three-cell summary of date, an attendee stepper, and the active duration, above a time-range tab selector, a divided list of room rows (each with capacity, equipment chips, hourly price, and a radio-style select indicator), closing on a sticky footer that prices the current pick and offers a reserve button.
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/booking10?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://ui.beste.co/r-base/booking10?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/booking10.tsx, plus the badge, button, and tabs shadcn/ui primitives it uses for the location and status badges, the reserve button, and the duration selector.
The installed file exports booking10Demo alongside the block: the exact props behind the preview above. Spread it to get a working room booking card in one line.
import { Booking10, booking10Demo } from "@/components/beste/block/booking10";
export default function BookingPage() {
return <Booking10 {...booking10Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Monitor, Video, Wifi } from "lucide-react";
import { Booking10 } from "@/components/beste/block/booking10";
export default function BookingPage() {
return (
<Booking10
badge={{ label: "Workspace", variant: "secondary" }}
heading="Reserve your <strong>meeting room</strong>"
spaceName="Vertex Coworking"
location="Floor 12"
date="April 18, 2026"
currency="$"
timeRanges={[
{ label: "1 hour", hours: 1 },
{ label: "Half day", hours: 4 },
]}
rooms={[
{
name: "Focus Pod",
floor: "12th Floor",
capacity: 4,
pricePerHour: 25,
equipment: [
{ icon: Monitor, label: "Display" },
{ icon: Wifi, label: "WiFi" },
],
available: true,
},
]}
labels={{ capacityPrefix: "Up to", perHourLabel: "/ hour", reserveButton: "Reserve Room" }}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
badge | { label: string; variant?: "default" | "secondary" | "outline" } | – | Centered eyebrow badge above the heading |
heading | string | – | Section heading, supports inline <strong> |
description | string | – | Muted paragraph under the heading |
spaceName | string | – | Title shown in the card header |
location | string | – | Outline badge with a pin icon in the card header |
date | string | – | Display string shown in the date summary cell |
currency | string | "$" | Symbol prefixed to every price |
rooms | MeetingRoom[] | [] | Rows rendered in the divided room list |
timeRanges | TimeRange[] | [] | Duration options rendered as tabs |
labels | Booking10Labels | {} | Static copy for the summary cells, badges, and button |
className | string | – | Extra classes for the outer <section> |
type MeetingRoom = {
name: string;
floor: string;
capacity: number;
pricePerHour: number;
equipment: { icon: LucideIcon; label: string }[];
available?: boolean;
};
type TimeRange = {
label: string;
hours: number;
};
type Booking10Labels = {
dateLabel?: string;
attendeesLabel?: string;
durationLabel?: string;
bookedBadge?: string;
maxPrefix?: string;
capacityPrefix?: string;
perHourLabel?: string;
noSelection?: string;
reserveButton?: string;
};selectedRoom (matched by room.name string, so two rooms sharing a name would both read as selected), selectedDurationIndex (defaults to 0, so the first timeRanges entry is preselected), and attendees (starts at 2).1 and 30 via Math.max/Math.min; there is no callback, so the count is purely internal display and filtering state.disabled when available === false or when its capacity is below the current attendees count; disabled rows drop to opacity-40, hide the select indicator, and show either the bookedBadge or a `${maxPrefix} ${capacity}` warning.heading is rendered through dangerouslySetInnerHTML, and its inline <strong> is styled bold and text-primary.pricePerHour × hours = totalPrice) when both a room and a duration are active, and the noSelection label otherwise.Button is disabled until a room is selected; clicking it only calls console.log("Room reservation:", {...}) with the current selection, so wiring an actual reservation is left to the integrator.max-w-lg.booking7
Hotel room selection with room type cards showing images, amenity icons, guest capacity, price per night, check-in/check-out date picker, guest counter, and total price summary.
booking6
Restaurant table reservation with guest count selector, date/time picker, seating preference (indoor/outdoor/bar), special requests textarea, and booking summary. Fine dining reservation flow.
booking9
Event ticket selection with event banner, date/venue info, ticket tier cards (General/VIP/Backstage) with perks list, quantity selector, and order total. Concert and conference ticket flow.
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.
booking2
Booking confirmation card with service details, date/time, provider info with avatar, price breakdown with subtotal/tax/discount/total, promo code input, cancellation policy, and confirm CTA.
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.