Session Picker

Choose a session

Pickthekindofhouryouneed.

You can change your mind after the first one. Most people do, at least once.

Twenty free minutes, on the phone

A short call to hear what brings you here and to say honestly whether we are the right room.

Length
20 minutes
Price
Free
Where
Phone or video
Reserve the call
About this block

Session PickerPRO

A booking chooser: a serif heading that settles in word by word and an intro, then a ruled list of session types on the left that swaps, with a soft blur, the muted detail card on the right showing a serif title, a description, a fact row and a sliding-marker pill.

Booking33: Session Picker

A session picker built as a ruled list beside a panel: choosing a name on the left blurs a new card into place on the right with its own title, description, three facts and booking action.

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/booking33?email=YOUR_EMAIL&license_key=YOUR_KEY"

Base UI flavor

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

This installs the block to components/beste/block/booking33.tsx plus the button22 sliding-marker pill and the text1 word stagger it uses.

Quick start

The installed file exports booking33Demo alongside the block: the exact props behind the preview above. Spread it to get a working session picker in one line.

tsx
import { Booking33, booking33Demo } from "@/components/beste/block/booking33";

export default function Page() {
  return <Booking33 {...booking33Demo} />;
}

Then replace the demo with your own props. Written out, a trimmed setup looks like this:

tsx
import { Booking33 } from "@/components/beste/block/booking33";

export default function Page() {
  return (
    <Booking33
      eyebrow="Choose a session"
      heading="Pick the kind of hour you need."
      description="You can change your mind after the first one. Most people do."
      types={[
        {
          name: "First conversation",
          title: "Twenty free minutes, on the phone",
          description: "A short call to hear what brings you here.",
          facts: [
            { label: "Length", value: "20 minutes" },
            { label: "Price", value: "Free" },
            { label: "Where", value: "Phone or video" },
          ],
          button: { label: "Reserve the call", href: "/call", tone: "dark" },
        },
      ]}
    />
  );
}

Props

PropTypeDefaultDescription
eyebrowstring–Small-caps line above the heading
headingstring–Section heading, rendered as an h2 through Text1
descriptionstring–Intro paragraph under the heading
typesSessionType[][]Session options; the first is selected on mount
classNamestring–Extra classes for the outer <section>
ts
type SessionType = {
  name: string;
  title: string;
  description: string;
  facts: { label: string; value: string }[];
  button: ActionButton;
};

type ActionButton = {
  label: string;
  href: string;
  tone?: "primary" | "light" | "dark" | "outline";
};

Behavior notes

  • The list is made of buttons carrying aria-pressed, not tabs. Each one swaps the panel beside it rather than revealing a hidden region, so a pressed toggle is the honest description of what happens.
  • Selection is tracked by index, so two session types may safely share a name without both lighting up at once.
  • The panel is keyed on the active index inside AnimatePresence with mode="wait", so the outgoing card finishes leaving before the next one blurs in and the two never overlap mid-swap.
  • The panel column carries min-h-[20rem], which reserves the height so a short option following a long one does not make the page jump under the reader's cursor.
  • Only the selected option's dot is opaque; the marker is always in the layout at opacity-0, so the row's width does not change when the selection moves.
  • The unselected names sit at text-foreground/50 and lift to full on hover, so the list reads as a set of choices rather than as a paragraph of headings.
  • Every card is its own h3 with its own facts and its own action, so different session types can point at different booking flows.

More Booking blocks

View all Booking
PRO

booking30

Session Request

PRO

booking24

Service And Slot Picker

PRO

booking27

Choose Your Clinician

Get Pro access