Skip to content

Hosted iframe

Embed SeatLayer's hosted buyer page, and understand what it can complete on its own and what it never tells your host page.

Updated View as Markdown

The hosted iframe is the lowest-code way to place a responsive SeatLayer picker on a page. It loads the published event at https://app.seatlayer.io/e/:eventKey and needs no browser credential.

It is a self-contained buyer page, not a component. That single fact decides both of the things worth knowing about it.

What it can complete

The framed page runs the same SeatPicker the SDK ships, so the buyer picks and holds seats for real — those seats are blocked for everyone else while the hold lasts.

Whether the buyer can also pay depends on the organizer’s account:

  • Payments enabled, and a gateway connected to the event — the buyer pays by card inside the frame, the money goes to the organizer’s own Stripe or Razorpay account, and the order is confirmed on the SeatLayer buyer page. The frame is served from app.seatlayer.io, so that is where the gateway returns the buyer. No host code is involved at any point.
  • Otherwise — at checkout the buyer sees SeatLayer’s “finish in the ticketing checkout” card. The seats stay held; nothing is charged.

What it never does

Use the iframe when the frame completing the sale (or completing nothing) is acceptable — a marketing page, a venue page, a preview, a layout review.

Use SeatPicker when your page needs to know. It works without a bundler through the hosted script, and gives you either the server checkout handoff (onCheckout) or, with checkout: "hosted", the picker’s own payment plus onOrderConfirmed in your own page.

Responsive embed

event-page.htmlhtml
<iframe
  id="seatlayer"
  title="Choose your seats"
  src="https://app.seatlayer.io/e/ev_9f3a"
  style="
    width: 100%;
    aspect-ratio: 4 / 3;
    max-height: min(85vh, 760px);
    border: 0;
    border-radius: 12px;
  "
  allow="fullscreen"
></iframe>

<script>
  const frame = document.getElementById("seatlayer");
  const seatLayerOrigin = new URL(frame.src).origin;

  addEventListener("message", (event) => {
    if (event.origin !== seatLayerOrigin) return;
    if (event.source !== frame.contentWindow) return;

    if (event.data?.type === "seatlayer:height") {
      frame.style.height = `${event.data.px}px`;
      frame.style.aspectRatio = "auto";
    }

    if (event.data?.type === "seatlayer:fullscreen") {
      const on = event.data.on === true;
      frame.style.position = on ? "fixed" : "";
      frame.style.inset = on ? "0" : "";
      frame.style.zIndex = on ? "9999" : "";
      frame.style.maxHeight = on ? "none" : "";
      document.documentElement.style.overflow = on ? "hidden" : "";
    }
  });
</script>

Replace ev_9f3a with a published event key. aspect-ratio reserves stable space before the first measurement arrives; the height message then tracks content changes.

Message contract

Message Payload Host action
seatlayer:height { type: "seatlayer:height", px: number } Set the frame height
seatlayer:fullscreen { type: "seatlayer:fullscreen", on: boolean } Pin or restore the frame

The picker sends these messages because a framed document cannot resize its own iframe or escape it for fullscreen. There is no public checkout, selection, hold, or booking-result message — a sale completed inside the frame is invisible to the host page by design, because the frame runs on SeatLayer’s origin and must not be able to assert anything to yours.

Use the frame helper

@seatlayer/js exports attachPickerFrame() for the same protocol. It validates the frame source, locks and restores document scrolling, preserves prior styles, handles height changes received during fullscreen, and exits fullscreen on Escape.

npm i @seatlayer/js
host-frame.tsts
import { attachPickerFrame } from "@seatlayer/js";

const iframe = document.querySelector<HTMLIFrameElement>("#seatlayer");
if (!iframe) throw new Error("SeatLayer iframe was not found");

const detach = attachPickerFrame(iframe);

// Call when the iframe is removed or the host view unmounts.
detach();

By default the helper accepts the origin parsed from iframe.src. To pin it explicitly:

const detach = attachPickerFrame(iframe, {
  origin: "https://app.seatlayer.io",
});

Security requirements

  • Compare event.origin with the iframe URL’s origin.
  • Compare event.source with iframe.contentWindow.
  • Accept only known message types and expected primitive fields.
  • Never put a SeatLayer secret key in the iframe URL or host JavaScript.
  • Give the iframe an accessible title.

Iframe versus SDK

Requirement Iframe SeatPicker
No bundler Yes Yes, through hosted script
Sells with no backend Yes, when payments are on Yes, with checkout: "hosted"
Buyer finishes on your domain No, always on app.seatlayer.io In-page gateways yes; a card redirect returns to app.seatlayer.io
Your payment/order step before booking No Yes
onCheckout, onOrderConfirmed, hold callbacks No Yes
Host pricing overrides No Yes
Host theme overrides No Yes
Responsive/fullscreen helper Required in host Built into same-document widget

Verification checklist

  • The host page does not need to know about holds or orders.
  • The event is published in the expected mode.
  • The listener verifies both origin and window source.
  • The frame has a useful title and keyboard focus is visible.
  • Height changes do not create horizontal page overflow.
  • Fullscreen locks scrolling and restores all prior styles on exit.
  • Mobile Safari and an Android browser have been tested.
  • Anything that must react to a hold or an order uses an SDK surface instead.

Continue with choose an integration or install SeatPicker.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close