Surface real-time booking signals so visitors feel the momentum — and convert faster.
14 people are looking at this right now
Offer ends in
—
Maria from Madrid just booked this
2 min ago
In high demand! Booked 23 times in the last 6 hours
Booking.com-style social proof ticker with rotating messages: live viewers, recent bookings, scarcity alerts, and location-based activity. Animated transitions, urgency indicators, and trust signals that boost conversion.
A Booking.com-style social proof panel that rotates a single animated ticker message (viewers, recent bookings, scarcity, location, or trending, each keyed to its own icon and color) on a timed interval with clickable dot navigation, stacked beneath a live "Live Activity" header and above a column of optional conversion cards: a per-second countdown, a recent-booking toast, a struck-through price comparison, a high-demand strip, an urgency alert, a three-column trust strip, a viewers avatar stack, a reviews summary, and a 24-hour hourly-bookings bar chart with per-bar tooltips.
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/booking13?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://ui.beste.co/r-base/booking13?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/booking13.tsx, plus the avatar, badge, and tooltip shadcn/ui primitives it uses for the viewer/review avatar stacks, the section and price badges, and the hourly heat-chart tooltips.
The installed file exports booking13Demo alongside the block: the exact props behind the preview above. Spread it to get a working social proof bar in one line.
import { Booking13, booking13Demo } from "@/components/beste/block/booking13";
export default function Page() {
return <Booking13 {...booking13Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Booking13 } from "@/components/beste/block/booking13";
export default function Page() {
return (
<Booking13
badge={{ label: "Social proof", variant: "secondary" }}
heading="Show <strong>live activity</strong>"
interval={4000}
messages={[
{ type: "viewers", text: "{highlight} people are looking right now", highlight: "14" },
{ type: "scarcity", text: "Only {highlight}", highlight: "2 slots left", time: "for your date" },
]}
priceComparisonVariant={{ currency: "$", originalPrice: 180, currentPrice: 129, saveLabel: "Save" }}
countdownVariant={{ label: "Offer ends in", endsAt: "2026-07-13T09:00:00Z", expiredLabel: "Offer ended" }}
labels={{ liveActivityLabel: "Live Activity", verifiedDataLabel: "Verified data" }}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
badge | { label: string; variant?: "default" | "secondary" | "outline" } | – | Section eyebrow badge above the heading |
heading | string | – | Section heading, supports inline <strong> |
description | string | – | Optional subheading paragraph under the heading |
messages | ProofMessage[] | [] | Rotating ticker messages cycled in the live panel |
interval | number | 4000 | Milliseconds between automatic ticker advances |
highDemandVariant | HighDemandVariant | – | Flame "in high demand" card |
urgencyVariant | UrgencyVariant | – | Rose urgency strip with a badge |
viewersVariant | ViewersVariant | – | Viewers count row with an avatar stack |
countdownVariant | CountdownVariant | – | Per-second countdown card |
recentBookingToast | RecentBookingToast | – | Single "just booked" toast card |
priceComparisonVariant | PriceComparisonVariant | – | Struck-through price with a computed savings badge |
trustStripVariant | TrustStripVariant | – | Three-column icon+label trust strip |
reviewsVariant | ReviewsVariant | – | Star rating and review-count summary |
heatVariant | HeatVariant | – | 24-hour hourly-bookings bar chart |
labels | Booking13Labels | {} | Header labels (liveActivityLabel, verifiedDataLabel) |
className | string | – | Extra classes for the outer <section> |
type ProofType = "viewers" | "recent" | "scarcity" | "location" | "trending";
type ProofMessage = {
type: ProofType;
text: string; // "{highlight}" placeholder is swapped for the highlight span
highlight?: string;
time?: string;
};
type HighDemandVariant = { title: string; description: string };
type UrgencyVariant = { text: string; badgeLabel: string };
type ViewersVariant = {
countLabel: string;
suffix: string;
avatars: string[];
extraCount: number;
};
type CountdownVariant = {
label: string;
endsAt: string; // ISO date string
expiredLabel: string;
};
type RecentBookingToast = {
name: string;
location: string;
avatar: string;
actionLabel: string;
timeAgo: string;
};
type PriceComparisonVariant = {
currency: string;
originalPrice: number;
currentPrice: number;
saveLabel: string;
};
type TrustSignal = { icon: LucideIcon; label: string };
type TrustStripVariant = { signals: TrustSignal[] };
type ReviewsVariant = {
rating: number;
count: number;
suffixTemplate: string; // "{count}" is replaced with count.toLocaleString()
avatars: string[];
};
type HeatVariant = {
label: string;
subtitle: string;
hourlyBookings: number[];
currentHour: number;
tooltipTemplate: string; // "{hour}" and "{count}" placeholders
};
type Booking13Labels = {
liveActivityLabel?: string;
verifiedDataLabel?: string;
};messages.length > 1: a setInterval(advance, interval) fades the current message out for 400ms, then bumps currentIndex to (prev + 1) % messages.length and fades back in. With one or zero messages no timer is set.useState and is never lifted out; there is no onChange callback, so a parent cannot observe or drive which proof message is showing. The dot buttons under the panel just set that same internal index.typeConfig map keyed by type; only scarcity (rose) and location (emerald) diverge from text-primary, so unlisted types would render nothing since config would be undefined.renderMessage splits text on the literal token {highlight} and injects a bold colored span; if highlight is omitted the raw text is shown verbatim and the token, if present, stays as literal text.remaining starts null (rendering an em-dash placeholder) and is only computed in useEffect after mount, ticking every second. It shows expiredLabel once endsAt passes, and prepends a Nd day segment only when days remain.heading is injected with dangerouslySetInnerHTML, so any inline <strong> (styled bold/primary) renders as markup rather than escaped text; pass trusted strings only.max((count / max) * 100, 6)% height so empty hours still show a 6% stub, highlights the bar at currentHour in primary, and exposes each hour through both an aria-label and a Radix Tooltip built from tooltipTemplate.trustStripVariant/heatVariant additionally require a non-empty array); passing none leaves just the live ticker panel. The whole header block (badge/heading/description) is likewise skipped when all three are absent.booking15
Post-booking success screen with animated check icon, confirmation number, booking summary (service, date/time, location, total), scannable QR code ticket, action buttons (calendar, receipt, directions), and email confirmation note.
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.
booking12
Airbnb/Booking.com-style search bar with location autocomplete, date range picker, guest counter dropdown, and search CTA. Responsive — inline on desktop, stacked on mobile. The universal booking hero section.
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.
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.