A clinician picker whose hairline list of practitioners, each showing their next opening, swaps an image tile floating a live availability card alongside their focus, background, and sites.
A clinician picker whose hairline list of practitioners, each showing their next opening, swaps an image tile floating a live availability card alongside their focus, background and sites.
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/booking27?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://ui.beste.co/r-base/booking27?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/booking27.tsx, the card31 person piece it floats on the tile (installed to components/beste/piece/card31.tsx), and the badge23 and button21 components it uses for the eyebrow and the actions.
The installed file exports booking27Demo alongside the block: the exact props behind the preview above. Spread it to get a working picker in one line.
import { Booking27, booking27Demo } from "@/components/beste/block/booking27";
export default function CliniciansPage() {
return <Booking27 {...booking27Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Booking27 } from "@/components/beste/block/booking27";
import { Card31 } from "@/components/beste/piece/card31";
export default function CliniciansPage() {
return (
<Booking27
badge={{ label: "Who you see" }}
heading="Pick a clinician, or let us pick the soonest"
description="Everyone here works across the same records."
anyoneTitle="No preference?"
anyoneBody="Taking the first available appointment usually gets you seen four days sooner."
anyoneButton={{ label: "Book the soonest slot", href: "/booking/soonest" }}
clinicians={[
{
name: "Dr Amelia Frost",
role: "Clinical lead",
avatar: { src: "/people/amelia.jpg", alt: "Portrait of Dr Amelia Frost" },
next: "Next free Wednesday",
focus: "Assessments and complex reviews",
bio: "Fourteen years in practice, most of it in assessment work.",
sites: "Bramble Health and Harbour Practice",
media: (
<Card31
avatar={{ src: "/people/amelia.jpg", alt: "Portrait of Dr Amelia Frost" }}
name="Dr Amelia Frost"
role="Clinical lead"
status="Next free Wednesday 09:00"
availability="soon"
rows={[{ label: "Appointment", value: "45 or 60 minutes" }]}
/>
),
},
]}
image={{ src: "/backdrops/blue.jpg", alt: "Soft blue gradient backdrop" }}
button={{ label: "See their availability", href: "/booking/availability" }}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
badge | { label: string } | – | Eyebrow above the hairline rule, rendered through Badge23 |
heading | string | – | Section heading in the left column of the header |
description | string | – | Supporting paragraph, right-aligned from md up |
anyoneTitle | string | – | Heading in the no-preference panel |
anyoneBody | string | – | Paragraph in that panel |
anyoneButton | { label: string; href: string } | – | Outline action for taking the soonest slot |
clinicians | Clinician[] | [] | Selectable practitioners, the first starts selected |
image | { src: string; alt: string } | – | Backdrop shared by every clinician's tile |
button | { label: string; href: string } | – | Action under the selected clinician's detail |
className | string | – | Extra classes for the outer section |
type ActionLink = {
label: string;
href: string;
};
type Clinician = {
name: string;
role: string;
avatar: { src: string; alt: string };
next: string;
focus: string;
bio: string;
sites: string;
media: ReactNode;
};media, so switching swaps the availability card entirely. The backdrop image is shared at block level, which keeps the tile steady while only the card changes.avatar appears both on the list row and inside the piece, the demo passes the same object twice. They are separate props, so keeping them in sync is on you.useState holding an index, starting at 0. There is no onChange prop and no controlled mode.bg-muted surface rather than colour alone, and rows carry px-4 so the fill extends past the text.next renders in the accent on every row, which makes availability the thing the eye lands on when comparing practitioners. That is the one accent use in the list.active.media is replaced outright, so a piece holding internal state resets when you switch away and back.button elements with aria-pressed and explicit cursor-pointer, since Tailwind v4 buttons default to cursor: default.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.
booking5
Staff/provider selection cards with avatar, name, specialty, star rating, review count, availability badge, experience years, and selected state. Grid layout with "Any Provider" option.
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.
booking26
A quiet booking picker whose left column lists each service as a single hairline row carrying only its length and price, swapping a sticky panel that floats a live week strip above the chosen service's detail and its book action.