Embedding Muin on Your Site
Drop a Muin smart form, donation page, giving page, or kiosk into any third-party site with a single iframe tag — no SDK, API key, or CORS setup.
Any Muin public surface under /go/* or /embed/* can be iframe-embedded on
a third-party site with a single <iframe> tag. There is no SDK, no API key,
and no CORS preflight to configure — the response headers do the work.
What you can embed
| Surface | Path shape | Examples |
|---|---|---|
| Smart forms | /go/<tenant>/form/<slug> | Volunteer signup, intake, contact |
| Donation pages | /go/<tenant>/donate/<slug> | One-time, sustainer, programs |
| Giving pages | /go/<tenant>/give | Campaign hubs |
| Kiosk | /go/<tenant>/kiosk | Tabletop / event sign-in |
| Embed-only widgets | /embed/<tenant>/<widget> | Donation block, progress bar |
Any other path (admin routes, authenticated dashboards, /api/*) is
deliberately blocked from iframe embedding — the response middleware sets
X-Frame-Options: DENY and Content-Security-Policy: frame-ancestors 'none'
on those.
Quick start: one-line iframe
<iframe
src="https://muin.falaah.ai/go/your-tenant/donate/sustainer?embedded=true"
width="100%"
height="700"
frameborder="0"
style="border: none; border-radius: 8px;"
title="Donate"
></iframe>
That’s the whole integration. The iframe loads cross-origin from muin.falaah.ai
and renders the form. Submissions hit Muin directly — your host page doesn’t
proxy anything.
Embed shapes
The same /go/* URL works in any of these placements. Live demos at
/live/embed-shapes:
- Inline iframe — drops into normal page flow at a fixed height.
- Side widget — sticky sidebar iframe, scroll-pinned.
- Modal — click a button,
<dialog>opens with the iframe inside. - Scroll-popup — iframe surfaces after the visitor scrolls past 50%.
- Exit-intent — iframe surfaces when the cursor leaves the top edge.
- Floating action button — a bottom-right circular button reveals a slide-up panel.
- Banner — a top strip with an embedded mini-form, dismissible for 7 days.
You can mix and match — a single page can have a banner, an inline embed, and a FAB simultaneously.
What gets passed through
?embedded=true— appended byembedUrl()as a marker query param. Drives no chrome changes today; it’s a forward-looking hook your host page can also key off (analytics, A/B tests).- Other query strings on the URL — preserved end-to-end. Use them to pre-fill
fields supported by
HiddenField. - Iframe-to-parent messages — when a form submits successfully, Muin posts a
muin:form-submittedMessageEventtowindow.parentso your host page can react (close the modal, fire confetti, redirect). See transitions.
What does NOT work in an embed
| Behavior | Why |
|---|---|
| Browser auth / login flows | Embedded forms run anonymously; no auth needed |
| Cookies (third-party blocked) | Forms don’t rely on cookies; tokens go in body |
| Top-level navigation from iframe | Sandboxed by browsers; trigger via postMessage |
| Forms behind tenant authorization | Use the standalone URL, not embed |
Building URLs programmatically
If you’re embedding from another Muin tool (a marketing site, a microsite), use the helpers instead of hardcoding the origin:
import { embedUrl, embedSnippet } from "@/lib/embed";
const url = embedUrl("/go/your-tenant/donate/sustainer");
// → "https://muin.falaah.ai/go/your-tenant/donate/sustainer?embedded=true"
const html = embedSnippet("/go/your-tenant/form/volunteer", {
height: 800,
title: "Volunteer signup",
});
// → "<iframe src='...' width='100%' height='800' ... title='Volunteer signup'></iframe>"
The helpers read PUBLIC_APP_URL from env so your dev / staging / prod
URLs stay consistent without code changes.
Live examples
/live/embed-shapes— same form, every shape./live/form-flavors— every form, swap-and-compare./live/in-context— fake nonprofit microsite using embeds in real-world placements.