Surveys · Last updated May 16, 2026
Embedding surveys
A survey doesn't have to live on a saveform.io link — one script tag renders it inline on your own page, styled and sandboxed, sized to its content. This page is the full embed reference: the tag, its attributes, the framework patterns, and what to check when something looks off.
The script tag
Copy the snippet from your survey's Share screen, or write it yourself — the only survey-specific part is the id:
<script src="https://saveform.io/embed.js" data-survey="YOUR_SURVEY_ID" async ></script>
The survey renders right where the tag sits. No wrapper element, no stylesheet, no initialisation call — the script derives its origin from its own src and everything else from the attributes.
Embed attributes
| Attribute | Effect |
|---|---|
data-survey | Required. The survey's id (from the Share screen or the dashboard URL). |
data-target | A CSS selector (e.g. "#survey") to mount the iframe inside, when the script tag can't sit where the survey should render — common in site builders that pin scripts to the footer. Defaults to right after the script tag. |
data-min-height | Initial height in pixels before the first auto-resize (default 320). Match it roughly to your survey's first screen to minimise layout shift. |
How the embed works
Under the hood the script does three things:
| Step | What happens |
|---|---|
| Inject | It creates an <iframe> pointing at the hosted survey in embed mode (standalone page chrome stripped) and mounts it after the tag or inside data-target. |
| Attribute | It forwards your page's hostname so responses are attributed to your site in the dashboard — inside an iframe the browser's own Origin header would say saveform.io. |
| Resize | The survey reports its content height via postMessage on every step change, and the script sets the iframe height to match — no fixed heights, no inner scrollbars, welcome screen through thank-you screen. |
React, Vue & Angular
SPA frameworks strip raw <script> tags from templates, so create the element in code after mount:
import { useEffect, useRef } from 'react';
export function SurveyEmbed() {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
const s = document.createElement('script');
s.src = 'https://saveform.io/embed.js';
s.async = true;
s.dataset.survey = 'YOUR_SURVEY_ID';
s.dataset.target = '#saveform-survey';
el.appendChild(s);
return () => { el.innerHTML = ''; }; // clean up on unmount
}, []);
return <div id="saveform-survey" ref={ref} />;
}The identical pattern works in Vue's onMounted and Angular's ngAfterViewInit — create the script element, set src, async and the data-* attributes, append it once. If your framework serves a static index.html you control, dropping the plain one-liner into the body works too. The framework guides cover the surrounding integration patterns.
WordPress & site builders
Anywhere you can paste HTML, the one-liner works as-is:
| Platform | Where to paste |
|---|---|
| WordPress | A "Custom HTML" block in the block editor, or a raw HTML widget in classic themes. |
| Webflow | An "Embed" element wherever the survey should sit. |
| Squarespace / Wix | A code/embed block. If the builder pins scripts to the footer, add an element with an id and point data-target at it. |
| Notion, Framer, … | Any embed surface that accepts raw HTML works the same way. |
Several embeds per page
Multiple script tags on one page are fine, including several different surveys. Each tag manages its own iframe, and resize messages carry the survey id, so two embeds never fight over each other's height.
Troubleshooting
| Symptom | Likely cause & fix |
|---|---|
| Nothing renders | The tag is missing data-survey (check the browser console for a SaveForm error), or your framework stripped the script tag — use the mount-in-code pattern above. |
| Iframe stays at initial height | Something on the page is filtering message events, or the embed was copied without the script (a bare iframe won't auto-resize). Always embed via embed.js. |
| Blocked by CSP | Sites with a strict Content-Security-Policy need frame-src https://saveform.io (and script-src allowing the embed script). |
| Responses not attributed to my site | Attribution uses the hostname the script reads at load time. Previewing from a local file (file://) has no hostname — deploy to a real origin (localhost is fine) and it appears. |