Skip to content
Documentation

Popup embed

Open the booking flow in an overlay from any button or link.

A popup keeps your page as it is and opens booking in an overlay — the right choice when the booking link is a call to action rather than the page’s purpose.

From a button

<button data-bunnycal-popup="acme/intro-call">Book a call</button>

<script async src="https://bunnycal.io/widget.js"></script>

Any element with data-bunnycal-popup becomes a trigger. The overlay traps focus while open, closes on Esc, and returns focus to the trigger.

Programmatically

BunnyCal.popup({
  link: "acme/intro-call",
  prefill: { name: "Ada Lovelace", email: "ada@example.com" },
});

Useful for opening booking after some other step completes — the end of a signup flow, for instance.

Events

Both embeds emit the same events:

EventFires whenPayload
readyThe widget has mounted
slotSelectedA guest picks a time{ start, end, timeZone }
bookedA booking is confirmed{ bookingId, start, end }
closedThe popup is dismissed{ booked: boolean }
BunnyCal.on("booked", ({ bookingId, start }) => {
  analytics.track("Meeting booked", { bookingId, start });
});

booked fires once per booking, after the server confirms it — so it’s safe to use for conversion tracking. closed carries whether a booking happened, which lets you distinguish an abandoned overlay from a completed one.

Don't book from the handler

The booked event is a notification, not a hook — the meeting is already confirmed and the guest already has the invite. For server-side work triggered by a booking, use a webhook instead, which is delivered reliably even if the guest closes the tab immediately.

Removing a widget

const widget = BunnyCal.inline({ target: el, link: "acme/intro-call" });
widget.destroy();

Call destroy() when unmounting the component that owns the container, otherwise the resize listener outlives the element it was measuring.