Embed SDK
The FormNode Embed SDK is a single 5KB script that drops any FormNode form into any page — inline in the layout, as a modal popup, or as a slide-in panel. The iframe auto-resizes to the form's content, prefill flows in cleanly, and a small postMessage bridge lets the host page react to load, submit, and close events.
Get the embed URL
Open a form in the dashboard, click Share, then the Embedtab. Pick a mode (inline, popup, or slide-in) and copy the snippet — it's already filled in with your form's URL. The examples below show what those snippets look like.
Inline mode
Renders the form directly in your page layout. The iframe auto-resizes as the form grows (e.g., dynamic fields revealing nested options) so there's no internal scrollbar — the form just takes the height it needs.
<div data-formnode-form="https://app.formnode.io/organizations/{org-id}/form/{form-id}"
data-mode="inline"></div>
<script src="https://app.formnode.io/embed.js"></script>Best for landing pages, contact pages, in-context intake (e.g., a ticket form embedded inside your help-desk article).
Popup mode
Opens the form in a centered modal overlay when the trigger element is clicked. The trigger is whatever element carries the data-formnode-form attribute — typically a button.
<button data-formnode-form="https://app.formnode.io/organizations/{org-id}/form/{form-id}"
data-mode="popup">
Open intake form
</button>
<script src="https://app.formnode.io/embed.js"></script>Closes on the Escape key, on backdrop click, or on submit. The backdrop is a translucent dark overlay; the modal width caps at 720px and stays scrollable on small screens.
Slide-in mode
Slides in from the right (or left, configurable) as a side panel, leaving the underlying page visible. Good for "request access" or "contact sales"-style CTAs where you don't want to cover the page's primary content.
<button data-formnode-form="https://app.formnode.io/organizations/{org-id}/form/{form-id}"
data-mode="slide-in">
Request access
</button>
<script src="https://app.formnode.io/embed.js"></script>Prefill
Two ways to prefill fields when the form loads:
Via data attribute
Add data-prefill as a JSON object on the same element. Each key matches a field name (not label):
<div data-formnode-form="https://app.formnode.io/..."
data-mode="inline"
data-prefill='{"company":"Acme","tech_email":"jsmith@msp.example"}'></div>Via URL parameters
Append ?field_name=value to the embed URL. This works the same way it does for direct-link forms — see Form builder & field types for the full pattern.
Imperative API
For dynamic mounting (single-page apps, modals you control yourself, forms gated behind auth state), use the global FormNode object the SDK exposes:
<script src="https://app.formnode.io/embed.js"></script>
<script>
// Inline render into a target selector
FormNode.inline('#intake-form', {
url: 'https://app.formnode.io/organizations/{org-id}/form/{form-id}',
prefill: { company: 'Acme', tech_email: 'jsmith@msp.example' },
});
// Popup, triggered by a button click
FormNode.popup({
url: 'https://app.formnode.io/organizations/{org-id}/form/{form-id}',
trigger: '#open-form-btn',
});
// Slide-in panel from the right
FormNode.slideIn({
url: 'https://app.formnode.io/organizations/{org-id}/form/{form-id}',
position: 'right',
});
</script>postMessage events
The embedded form posts events back to the host page so you can react to load, resize, submit, and close. Listen on window:
window.addEventListener('message', (event) => {
if (event.origin !== 'https://app.formnode.io') return;
if (!event.data || typeof event.data !== 'object') return;
switch (event.data.type) {
case 'formnode:ready':
// Form has rendered and posted its initial height
console.log('Form ready, height:', event.data.height);
break;
case 'formnode:resize':
// The SDK already handles this; only listen if you want to react
console.log('Form resized to', event.data.height);
break;
case 'formnode:page-change':
// User moved to a different page in a multi-page form
console.log('Page changed to', event.data.page);
break;
case 'formnode:submit':
console.log('Submission ID:', event.data.submissionId);
// Hide the embed, show a thank-you, fire your analytics, etc.
break;
case 'formnode:close':
// Popup or slide-in was dismissed
break;
}
});Always check the origin against https://app.formnode.io (or your custom domain) before trusting event.data. The SDK ignores messages from other origins automatically, but your own listener should too.
Using a custom domain
If you've configured a custom domain, replace app.formnode.io with your domain in both the form URL and the script URL — for example forms.yourcompany.com/embed.js. The browser sees a single origin, which avoids any third-party-cookie concerns.
Troubleshooting
Form loads but doesn't resize
The SDK uses postMessage to negotiate height. If your page has a strict Content Security Policy that blocks frame-src for app.formnode.io, the iframe never sets its height. Add app.formnode.io (or your custom domain) to frame-src in your CSP.
Prefill values don't appear
Prefill keys match the field name, not its label. If your field is labeled "Company name" and named company_name, you must prefill with company_name, not company or Company name.
Popup opens behind a different overlay
The SDK's modal and slide-in panel use z-index: 99999. If your site uses higher z-indexes for navigation or other modals, increase yours above 100000 so the embed sits on top.