Skip to main content

Booking Embed

The booking embed lets you display the SavvyCal appointment booking experience directly on your website. It's a small, SavvyCal-hosted web component that wraps an <iframe> pointing at a booking URL. The booking interface runs inside the iframe; the component handles sizing, theming, and lifecycle events.

There are two ways to embed:

  • Inline — the booking interface renders directly in the page.
  • Popup — a trigger opens the booking interface in a full-screen overlay.

This is version 2 of the booking embed. The previous <savvycal-booking-embed> component is still supported and documented in Booking Embed (v1), but we recommend new integrations use the component described here.

Live Demo

Inline — the booking interface renders directly in the page:

Popup — a trigger opens the booking interface in a full-screen overlay:

Book an appointment

Installation

Before embedding, add your site's domain to your Allowed Origins list on the Settings → Allowed origins page (just the hostname, e.g. myapp.com or subdomain.myapp.com). If you're using localhost, include the port if it differs from 80 (e.g. localhost:3000).

Include the embed script once per page:

<script async src="https://js.savvycal.app/v2/embed.js"></script>

This registers the <savvycal-booking> and <savvycal-booking-button> elements and the SavvyCal JavaScript API.

Booking URLs

Both embed types load a booking URL that you supply via the src attribute. Booking URLs come from booking links. You can create a booking link when editing a service, under the Booking Links tab, or via the Booking Links API.

Customize the booking experience by adding query parameters to the booking URL:

ParameterValuesDescription
themelight or darkColor theme of the booking interface. Defaults to light.
show_headerfalseHide the service name and duration shown above the form. Shown by default.
localeauto, en, or esInterface language. Defaults to auto, which matches the visitor's browser.

For example, a dark-themed embed with the header hidden:

YOUR_BOOKING_URL?theme=dark&show_header=false
note

The component automatically appends a few parameters of its own (the embed mode, a per-instance identifier, and the host origin) when it loads the iframe. You don't need to set these yourself.

For privacy, the booking URL does not accept prefilled client details (name, email, phone, etc.) via query parameters. Client details are collected within the booking flow.

Inline embed

Add a <savvycal-booking> element where you want the booking interface to appear:

<script async src="https://js.savvycal.app/v2/embed.js"></script>

<savvycal-booking
src="YOUR_BOOKING_URL?theme=light"
></savvycal-booking>

The inline embed renders with a transparent background and no border or padding, fills the width of its container, and grows to fit its content. Wrap it in your own container to control its width and placement.

Use a <savvycal-booking-button> element as a trigger. Clicking it (or pressing Enter or Space) opens the booking interface in a full-screen overlay:

<script async src="https://js.savvycal.app/v2/embed.js"></script>

<savvycal-booking-button
src="YOUR_BOOKING_URL?theme=light"
>
Book an appointment
</savvycal-booking-button>

The element itself is the clickable trigger — style it to match your site with your own CSS. Don't place a <button> inside it; the element already behaves as a button.

To open the popup from your own code (for example, from an existing button), call:

SavvyCal.openPopup("YOUR_BOOKING_URL?theme=light");

The popup closes when the visitor clicks outside the booking card or presses Escape. You can customize the dimmed backdrop with the --savvycal-backdrop-color CSS variable:

:root {
--savvycal-backdrop-color: rgba(0, 0, 0, 0.6);
}

Events

The embed reports lifecycle events to the host page as DOM events. Listen for them on window:

EventDescription
savvycal:appointment.createdFired when a visitor completes a booking.
window.addEventListener("savvycal:appointment.created", (e) => {
const { appointmentId } = e.detail;

// Example: redirect to a thank-you page
// window.location.href = "/thank-you?appointment=" + appointmentId;
});
note

savvycal:appointment.created carries only the new appointment's ID. It shares its name with the appointment.created webhook event but not its payload — fetch full appointment details from your server via the API if you need them.

All embed events are namespaced with a savvycal: prefix and dispatched as DOM events of the same name, so you can listen for new events as they're introduced.