Building embeddable page templates

How to build page templates that work when embedded on external websites via iframe

Movement pages can be embedded on external websites using an iframe. When embedded, only the form content is shown — the surrounding chrome is automatically hidden.

This guide covers how to build templates that adapt to an embedded context.

Getting started

To enable embedding for your account, contact us with the domain(s) you'd like to embed pages on (e.g. yourwebsite.org). These need to be whitelisted before embeds will work.

Once enabled, you can get the embed snippet from the Embed button in the page builder, or from the Manage dropdown on the page overview.

The embed URL

Every page has an embed variant at {publicUrl}/embed. This URL:

  • Hides the header and language switcher
  • Sets flow.is_embedded to true in the Liquid context

The embed snippet generated in the UI produces an iframe like:

<iframe src="https://your-domain.com/p/my-page/embed" style="width:100%;border:0;min-height:600px;" loading="lazy"></iframe>

Detecting embed mode in Liquid

Use flow.is_embedded in your Body HTML to conditionally show or hide content:

{% if flow.is_embedded == true %}
  <div class="embedded-header">
    <h2>{{ template.heading }}</h2>
  </div>
{% else %}
  <div class="full-header">
    <img src="{{ template.logo }}" />
    <h1>{{ template.heading }}</h1>
    <nav>...</nav>
  </div>
{% endif %}

This is useful for:

  • Hiding navigation and branding that doesn't make sense inside an iframe
  • Simplifying the layout for a more compact embedded form
  • Adjusting spacing and padding for a tighter fit
  • Hiding footers or disclaimers that are already on the host page

Template design tips for embeds

Keep it compact

Embedded forms typically sit within a section of a larger page. Avoid large hero images, excessive padding, or full-page layouts when in embed mode.

{% if flow.is_embedded != true %}
  <div class="hero">
    {{ template.heroImage }}
  </div>
{% endif %}

<div class="form-content">
  <surveyform></surveyform>
</div>

Use responsive widths

The iframe is set to width: 100%, so the form takes the width of its container on the host page. Use percentage-based or max-width layouts rather than fixed pixel widths:

.form-content {
  max-width: 600px;
  margin: 0 auto;
  padding: 16px;
}

Handle the iframe height

The iframe has min-height: 600px by default. If your form is taller, you may need to adjust this on your website. Consider keeping embedded forms concise — fewer questions per page, minimal vertical spacing.

JavaScript events in embeds

All page template events (movement:page:loaded, movement:action:changed, etc.) fire inside the iframe as normal. The host page cannot listen to these directly due to cross-origin restrictions, but you can use postMessage in your template's Head HTML to relay events to the parent:

<script>
  document.addEventListener('movement:action:changed', function(event) {
    window.parent.postMessage({
      type: 'movement:action:changed',
      detail: event.detail
    }, '*');
  });
</script>