Embedding the widget

IT / TechnicalMarketing

A single script tag turns any element on your site into a Preb booking widget. You can embed it inline (the calendar renders right in the page) or as a popup (a button opens it in a modal). There is no config object and no build step — everything is controlled with data-* attributes.

Generate it for you

You don't have to hand-write any of this. Open an event type, go to the Embed tab, pick a mode, and copy the ready-made snippet. This page explains what that snippet does so you can customise it.

Inline embed

Add the loader script once, then give any element a data-preb-link attribute. Preb finds it on page load and mounts the widget inside it.

<div
  data-preb-link="acme/intro-call"
  style="min-height:600px;border-radius:8px;overflow:hidden"
></div>
<script src="https://app.preb.co/embed.js" async></script>

The value of data-preb-link is your booking link. It has two valid shapes:

  • Personal: username/event-slug (e.g. acme/intro-call)
  • Team: team/team-slug/event-slug

Only lowercase letters, numbers, hyphens, and underscores are allowed in a link. An invalid link is ignored (with a [Preb] warning in the browser console) rather than rendered, so a typo can never inject anything into your page.

If the container has no height (or is shorter than 100px), Preb gives it a 600px starting height and then resizes it to fit — see Auto-resize.

Pointing at a different host

By default the widget loads from https://app.preb.co. If you run Preb on a custom domain, add data-preb-host="https://your-domain.com" to the same element. Only http/https origins are accepted; anything else falls back to the default.

For a "Book a meeting" button that opens the widget in a centered modal, use data-preb-popup instead. It works on any element — usually a button.

<button data-preb-popup="acme/intro-call">Book a meeting</button>
<script src="https://app.preb.co/embed.js" async></script>

Use popup mode on Wix

Wix renders embedded HTML in a foreign-origin frame, which breaks the inline widget's automatic resizing. On Wix, use popup mode — it renders in its own modal and isn't affected.

Floating button

For a button that stays in the bottom corner of every page, use data-preb-floating. It opens the same modal as the popup embed.

<div
  data-preb-floating="acme/intro-call"
  data-preb-text="Book a call"
  data-preb-position="bottom-right"
></div>
<script src="https://app.preb.co/embed.js" async></script>
  • data-preb-text — the caption (default "Termin buchen").
  • data-preb-positionbottom-right (default) or bottom-left.
  • data-preb-color / data-preb-text-color — button background and text colour; any CSS colour value. Defaults are a near-black pill with white text.
  • data-preb-prefill and data-preb-consent work exactly as on the other embeds.

One floating button per page: further elements are ignored (marked data-preb-mounted="duplicate"). Add the snippet to your site's footer to show it everywhere.

Loading state

The script paints a skeleton of the widget the instant it runs — rounded card, header, summary and form panels — and keeps it until the real widget reports its first height, then crossfades. Visitors never see an empty box, and the container never snaps to size. The skeleton's card colour comes from data-preb-shell (a CSS colour; default #111111), which the Embed tab adds automatically when your widget uses a light or custom shell. Since it is baked into the snippet, copy the code again after changing the widget colour.

Site builders that wrap embeds in a frame

Some builders — Framer's Embed element in particular — run custom HTML inside an iframe of their own. Preb detects that automatically: as long as the wrapper is same-origin with your page (Framer's is), the widget still reads campaign tags and click IDs from your real page URL, opens its popup over the whole page, exposes window.Preb to your page's scripts, and grows the wrapper frame so nothing is clipped. Just paste the complete snippet into the embed element — no head code needed. Wix's wrapper is cross-origin, which nothing can reach through; that's why Wix gets the popup recommendation above.

The embed snippet

The script can be included once per page; a guard prevents it from initialising twice if it appears more than once. Three snippet styles are available from the Embed tab:

StyleWhat you getAuto-resizeConversion event
InlineWidget rendered in a data-preb-link element
PopupButton that opens the widget in a modal
Floating buttonFixed corner button that opens the modal
Plain iframeA raw <iframe> you paste yourself

The plain-iframe option is for environments where you can't load the script at all:

<iframe
  src="https://app.preb.co/acme/intro-call?embed=true"
  width="100%"
  height="700"
  frameborder="0"
  style="border-radius:8px"
></iframe>

Plain iframe has trade-offs

A raw iframe does not auto-resize, does not fire the bookingSuccessful event, and does not forward campaign tags or ad click IDs (gclid, fbclid, utm_*) from your page — Google and Meta can't credit a booking to an ad. You must set a fixed height (the snippet uses 700). Prefer the inline or popup script unless your platform blocks third-party scripts.

Auto-resize

With the inline or popup script, the widget measures its own content and tells the parent page how tall it should be, so there are never inner scrollbars or clipped content. It does this by posting a message to the parent window:

{ preb: true, action: "resize", height: 812 }

embed.js listens for this message only from iframes it created and on allowed origins, then sets the container's height. You don't need to do anything — it's automatic. This is also why the plain-iframe option can't resize: there's no script on the page to receive the message.

Prefilling

You can pre-populate the first-step fields (name, email, phone) so a known visitor doesn't have to retype them. Add a data-preb-prefill attribute with semicolon-separated key=value pairs:

<div
  data-preb-link="acme/intro-call"
  data-preb-prefill="name=Jane Doe;email=jane@acme.com;phone=+49123456789"
></div>

Only name, email, and phone are honoured (other keys are dropped), and values are length-capped (name 120, email 254, phone 40 characters). When you toggle Pre-fill from the page URL in the Embed tab, the snippet uses {{name}} / {{email}} / {{phone}} placeholders for you to replace with your site's template variables.

The hosted booking page accepts the same values as repeatable query parameters, which is what the widget passes through to the iframe:

https://app.preb.co/acme/intro-call?prefill=name=Jane&prefill=email=jane@acme.com

Verify the install

Once the snippet is live, load the page once. Within seconds the event type's Embed tab shows "Seen on your website" with the site and the embed mode — no developer tools needed. The script sends one small beacon per page load (/api/public/embed/ping) carrying the page origin, the booking link, the embed mode and the script build. It contains no visitor data.

Which version is my page running?

The script is served from one unversioned URL so every page keeps receiving fixes, and browsers re-check it every five minutes. To see the build a page is running, open the browser console on that page and type:

Preb.version   // e.g. "0.1.0+816e278"

The embedded widget never shows Preb's cookie banner — your page owns consent. Hand the visitor's decision to the widget with Preb.consent('granted') from your own banner, or declare it on the element with data-preb-consent="granted" when your site needs no banner. Without either, the widget runs with consent denied (no pixels, no funnel events). Details and the exact behaviour: Tracking inside embeds.

Styling notes

  • Theme. The widget renders its own card on a transparent background, so it blends into your page. There are no colour or theme attributes — sizing is automatic and styling is handled inside the widget.
  • Attribution is forwarded automatically. When the inline or popup script mounts, it copies known ad/marketing parameters from the parent page's URL onto the booking iframe: utm_source, utm_medium, utm_campaign, utm_content, utm_term, fbclid, gclid, ttclid, and li_fat_id. All other parameters are stripped. (If you instead link to the hosted page from a plain button, copy the Embed tab's link-forwarding snippet.)
  • React to a successful booking. The script exposes a tiny API so you can fire your own analytics or thank-you logic when a booking completes:
<script>
  window.Preb = window.Preb || {
    _q: [],
    on: function (a, b) { this._q.push([a, b]); },
  };
  window.Preb.on("bookingSuccessful", function (data) {
    console.log("Booked!", data.uid, data.startTime);
  });
</script>

Define this before embed.js loads — the stub queue above captures early calls and replays them once the script is ready. Besides bookingSuccessful you can subscribe to the four preb_* funnel events — see Tracking inside embeds. The bookingSuccessful payload is:

{
  uid: string;        // the booking's unique id
  title: string;
  startTime: string;  // UTC ISO-8601
  endTime: string;    // UTC ISO-8601
  location: { kind: string; value?: string; conferencingLink?: string };
  eventTypeId: string;
  eventTypeSlug: string;
  eventName: string;
}

This event only fires with the inline or popup script (it rides the same parent/iframe message channel as auto-resize).

See also: Public API reference for the server-side endpoints, and Data & privacy for how booker data is handled.