Docs/Embed Widgets

Embed Widgets

Put a Modality event or form directly on your own website, sell tickets, collect RSVPs, or take form submissions without sending visitors anywhere else. Copy one snippet from the Share dialog, paste it into any page, and the widget renders inline, as a popup button, or as a basic iframe. Paid checkout runs on Stripe inside the widget, so payment never leaves your site.

GIF (FLAGSHIP, currently no media): open an event's Share dialog, switch to the Link tab, toggle the three embed modes, copy the Popup-button snippet, paste it into a plain HTML page, then click the 'Get Tickets' button and complete Stripe Embedded Checkout inside the modal.

Demo GIF / screenshot to be added

What You Can Embed

Two kinds of things embed with the exact same snippet shape, differing only by one data attribute:

  • An event, a compact ticket/RSVP widget (data-es-event="slug"). Buyers pick tickets or RSVP and pay inline.
  • A form, any form you built in the form builder (data-es-form="slug"), rendered with its own theme and submitting to Modality.

Both are served with frame-ancestors * (and X-Frame-Options: ALLOWALL), so they can be iframed on any domain, your marketing site, a Squarespace/Wix/WordPress page, a Notion embed, anywhere you can paste HTML.

Getting the Snippet

The snippet is generated for you, you never hand-write it. For an event:

  1. 1

    Open the Share dialog

    On any published event, click Share. (Events must be published for the embed to render; draft events 404 in the frame.)

  2. 2

    Go to the Link tab

    The Link tab holds the public URL, printable QR codes, and, under Embed on your website, the snippet generator.

  3. 3

    Pick a mode

    Toggle between Popup button, Inline, and Basic iframe. The code box updates live and a one-line description explains each mode.

  4. 4

    Copy and paste

    Click Copy and paste the snippet into your site's HTML where you want the widget to appear. That's the whole install, no build step, no npm package.

Forms embed the same way, the form builder's share/embed surface produces the equivalent snippet with data-es-form in place of data-es-event. Everything below (modes, the loader script, the resize protocol) is shared between the two.
The Share dialog's Link tab showing the 'Embed on your website' section, the Popup button / Inline / Basic iframe mode toggle, the per-mode helper text, and the generated code block with the Copy button.
The Share dialog's Link tab showing the 'Embed on your website' section, the Popup button / Inline / Basic iframe mode toggle, the per-mode helper text, and the generated code block with the Copy button.

The Three Modes

All three point at the same underlying widget; they differ only in how it appears on your page.

1. Popup button

A styled button sits on your page; clicking it opens the compact ticket/RSVP widget in a centered modal over a dimmed backdrop. Closes on the ✕, on backdrop click, or with Esc. This is the default mode in the Share dialog.

<button data-es-event="my-event-slug" data-es-ws="your-workspace" data-es-mode="popup">Get Tickets</button>
<script src="https://app.modalitystudio.com/embed.js"></script>

2. Inline widget

The widget renders directly in the flow of your page inside a <div> and auto-resizes to fit its content (no inner scrollbar). This is the mode with data-es-mode omitted, standard is the default.

<div data-es-event="my-event-slug" data-es-ws="your-workspace"></div>
<script src="https://app.modalitystudio.com/embed.js"></script>

3. Basic iframe

A plain fixed-height <iframe> of the widget, no loader script, no auto-resize. Use it where you can only paste an iframe (some page builders) or when you want a fixed box. It carries allow="payment; clipboard-write" so Stripe wallet buttons and copy-link work inside the frame.

<iframe
  src="https://app.modalitystudio.com/event/my-event-slug/embed?ws=your-workspace"
  width="100%" height="600" frameborder="0"
  style="border:none;border-radius:12px;"></iframe>
The loader also supports a fourth, full-page mode (data-es-mode="fullpage") that pins the widget over the whole viewport. It isn't offered in the Share dialog, but it's available if you write the tag by hand.

Screenshot: a plain external web page rendered three times side by side, the Popup-button variant (button + open modal), the Inline variant (widget in the page flow), and the Basic-iframe variant (fixed box), to contrast the three modes.

Demo GIF / screenshot to be added

The Loader Script & Data Attributes

The Popup and Inline snippets include a tiny loader, /embed.js. It scans your page for embed elements, builds the iframe, wires up the modal, and handles auto-resize. It self-initializes on DOM ready, guards against double-loading, and is safe to include once even with several widgets on the page.

Element attributes

  • data-es-event="slug", embed an event's compact ticket/RSVP widget. The presence of this attribute is what makes the element an event embed.
  • data-es-form="slug", embed a form instead. The kind is derived from whichever attribute is present, so an event and a form embed can never be confused.
  • data-es-ws="workspace-slug", scope the lookup to your workspace (see Workspace Scoping).
  • data-es-mode="popup" | "standard" | "fullpage", how it renders. Omitted means standard (inline).

Script attribute

  • data-es-url="https://app.modalitystudio.com", an optional override on the <script> tag. Normally the loader infers the base URL from its own src (by stripping /embed.js), so you only set this if you load the script from a different origin than the app.

In-Modal Stripe Checkout

The point of the embed is that buyers never leave your site to pay. Here's what happens when someone checks out inside the widget:

  1. They select ticket quantities (the widget honors the required-base floor and per-tier availability) and enter their email and name.
  2. On submit, the widget calls checkout in embedded mode and gets back a Stripe client secret.
  3. The widget signals the host page to grow the iframe fullscreen, then mounts Stripe Embedded Checkout right there, the payment form renders inside the modal, on your domain, with no redirect.
  4. On success the buyer sees a confirmation ("Registration complete!") and a link to the full event page; a confirmation email goes out automatically.

Free tickets & RSVPs skip Stripe entirely and complete immediately. If your workspace isn't on Stripe Connect (so Embedded Checkout can't render in a frame), the widget falls back to opening Stripe's hosted checkout page in a new tab instead.

Because the popup's modal grows the existing iframe rather than reloading it, an in-progress Stripe checkout is never destroyed mid-payment. Closing the modal restores the widget and the host page's original scroll position.

GIF: inside the Popup embed on an external site, select 2 tickets, enter email, hit Checkout, and watch the modal expand to fullscreen with Stripe Embedded Checkout mounted inline, ending on the confirmation screen.

Demo GIF / screenshot to be added

RSVP & Free-Event Widgets

When an event has no ticket types (or RSVP is enabled), the same embed renders as an RSVP widget instead of a ticket picker: Going / Maybe / Can't Go buttons plus name and email. Submitting posts the RSVP and shows a "You're in!" confirmation, no payment step. This is automatic; you use the identical snippet.

Multi-Showtime Events

For a recurring / multi-showtime event, the buyer must choose which showing to buy for (each showing has its own inventory). The compact embed can't safely collect that choice, so instead of a ticket picker it shows a "Multiple showtimes available" card with a Pick a time & get tickets button that links out to the full event page, where the buyer selects a date and checks out normally. No configuration needed; the widget detects the showings and switches to link-out mode on its own.

The embed widget in its multi-showtime state, event title/date/venue header, a 'Multiple showtimes available' card, and the 'Pick a time & get tickets' button that links to the full event page.
The embed widget in its multi-showtime state, event title/date/venue header, a 'Multiple showtimes available' card, and the 'Pick a time & get tickets' button that links to the full event page.

Workspace Scoping

Slugs are unique within a workspace, not globally, two workspaces could each have an event slugged summer-fest. The data-es-ws attribute (rendered into the iframe URL as ?ws=your-workspace) tells the embed which workspace to resolve the slug against, so it always loadsyour event. The Share dialog fills this in for you when it knows your workspace slug; keep it in the snippet you paste.

For the basic iframe, the scope rides along in the URL itself as /event/<slug>/embed?ws=<workspace>. For the popup and inline snippets, the loader reads data-es-ws off your element and appends it to the iframe URL for you.

Listening for Submissions (Advanced)

The loader and the widget talk over postMessage using an eventstudio:* protocol, ready, resize, modal-open, and modal-close drive the auto-resize and fullscreen behavior for you. You don't manage any of that.

For inline widgets, the loader also dispatches a DOM es:submit event on your container element when a submission completes, which you can listen for to fire your own analytics or UI:

<div id="rsvp" data-es-event="my-event-slug" data-es-ws="your-workspace"></div>
<script src="https://app.modalitystudio.com/embed.js"></script>
<script>
  document.getElementById('rsvp').addEventListener('es:submit', function () {
    // e.g. gtag('event', 'rsvp_complete')
  });
</script>
Every widget carries a small "Powered by Modality" credit at the bottom. Prices are shown in the event's currency, and the platform service fee (if any) is disclosed at checkout, exactly as on the full public page.