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:
| Event | Fires when | Payload |
|---|---|---|
ready | The widget has mounted | — |
slotSelected | A guest picks a time | { start, end, timeZone } |
booked | A booking is confirmed | { bookingId, start, end } |
closed | The 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.
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.