A no-availability state that answers with three hairline figures and a numbered explanation of how offers are made, beside a sticky square tile floating a live panel of the waiting list itself and an urgent fallback.
A no-availability state that answers with three hairline figures and a numbered explanation of how offers are made, beside a sticky square tile floating a live panel of the waiting list itself and an urgent fallback.
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.
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/booking28?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://ui.beste.co/r-base/booking28?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/booking28.tsx, the dashboard33 metrics piece it floats on the tile (installed to components/beste/piece/dashboard33.tsx), and the badge23 and button21 components it uses for the eyebrow and the action.
The installed file exports booking28Demo alongside the block: the exact props behind the preview above. Spread it to get a working state in one line.
import { Booking28, booking28Demo } from "@/components/beste/block/booking28";
export default function WaitingListPage() {
return <Booking28 {...booking28Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Booking28 } from "@/components/beste/block/booking28";
import { Dashboard33 } from "@/components/beste/piece/dashboard33";
export default function WaitingListPage() {
return (
<Booking28
badge={{ label: "Nothing free this week" }}
heading="Join the waiting list and you will hear before anyone else does"
description="Cancellations are offered to this list in order, automatically."
figures={[
{ value: "11 min", label: "median time from a cancellation to an offer" },
{ value: "84%", label: "of cancelled slots are refilled from this list" },
{ value: "3 days", label: "median wait for people who join it" },
]}
stepsTitle="How it actually works"
steps={[
{
title: "You tell us what suits",
detail: "Days, times, sites, and which clinicians you are happy to see.",
},
{
title: "A cancellation is offered to the whole list",
detail: "Everyone whose preferences fit gets it at the same moment.",
},
]}
media={
<Dashboard33
title="The list right now"
range="This week"
items={[{ label: "Waiting", value: "34", delta: "+6", direction: "up" }]}
/>
}
image={{ src: "/backdrops/green.jpg", alt: "Deep green gradient backdrop" }}
fallbackTitle="If it is urgent"
fallbackBody="Ring the practice rather than waiting on this list."
button={{ label: "See all available times", href: "/booking" }}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
badge | { label: string } | – | Eyebrow above the heading, rendered through Badge23 |
heading | string | – | Section heading |
description | string | – | Supporting paragraph, capped at max-w-xl |
figures | Figure[] | [] | Evidence that the list works, on a hairline row |
stepsTitle | string | – | Heading above the numbered explanation |
steps | Step[] | [] | How offers are made, numbered automatically |
media | ReactNode | – | Live asset on the square tile, dashboard33 in the demo |
image | { src: string; alt: string } | – | Backdrop behind the media tile |
fallbackTitle | string | – | Heading in the urgent-case panel |
fallbackBody | string | – | What to do instead if it cannot wait |
button | { label: string; href: string } | – | Outline action under the panel |
className | string | – | Extra classes for the outer section |
type ActionLink = {
label: string;
href: string;
};
type Figure = {
value: string;
label: string;
};
type Step = {
title: string;
detail: string;
};String(index + 1).padStart(2, "0"), so the list always reads 01, 02 and renumbers itself when reordered.aspect-square rather than a fixed height, so it scales with the column width and keeps its proportions from mobile through desktop.lg:sticky lg:top-24 lg:self-start. The self-start is required, otherwise the column stretches to the row height and sticky has nothing to travel within. Adjust top-24 to match your own fixed header.border-t with no closing rule, so the left column reads as one continuous run.booking29
Three intake questions that land on a real appointment type instead of a drop-down: the answers pick a route with its length, its facts, and the clinician you will sit with, and the image tile beside them swaps its floated availability card to match.
booking24
An interactive appointment picker: a hairline panel pairing a selectable service list with a scrollable day strip and a time slot grid that marks unavailable times, beside a full-height provider photo with a gradient name card, closing on a live summary bar and confirm action.
booking25
A confirmation screen with an accent eyebrow, a two-column hairline table of the appointment details, a checked list of what to bring, and a sticky tile floating the live phone view the member also receives.
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.