BETA
Skip to content

Embedding

Drop a Krafter survey onto any web page. The embed script is a thin wrapper that renders the survey's public page (/s/:slug) inside an iframe — inline, in a popup, or wherever you mark up a container.

Embed surface stability

The embed script API (data attributes, modes, host override) may evolve before reaching v1. Lock-step semver is not committed yet — pin the script tag to a specific commit if you need byte-stable behavior, or expect that today's data-mode values may grow over time.

The script

html
<script src="https://app.krafter.dev/embed/surveys.js" defer></script>

That's the whole installation. The script:

  1. Waits for DOMContentLoaded
  2. Finds every element with a data-krafter-survey="<slug>" attribute
  3. Renders the survey based on data-mode (inline iframe, popup trigger)
  4. Listens for postMessage from the iframe to auto-resize inline embeds

The script is served from the same origin as the API, with Cache-Control: public, max-age=3600 and CORS-permissive headers, so a single edge cache hop covers most page loads.

Host override

Self-hosting Krafter on a custom domain? Pass data-host on the script tag — the embed will rewrite the iframe URL accordingly:

html
<script
  src="https://my-krafter.example.com/embed/surveys.js"
  data-host="https://my-krafter.example.com"
  defer></script>

The default is https://app.krafter.dev. If you don't override, all iframes load from app.krafter.dev.


Inline embed

Drop the script anywhere in the page, then add a container where the survey should render:

html
<!-- Anywhere on the page -->
<div data-krafter-survey="product-feedback"></div>

<!-- Once at the bottom of <body> -->
<script src="https://app.krafter.dev/embed/surveys.js" defer></script>

The container is replaced with an iframe pointing at https://app.krafter.dev/s/product-feedback?embed=true. Default styling:

  • width: 100%
  • border: none
  • min-height: 500px
  • loading="lazy" so the iframe defers loading until it nears the viewport

The embed script auto-resizes the iframe height as the respondent moves through questions, by listening to postMessage events of shape { type: "krafter-survey-resize", height: <px> } from the iframe. You don't have to wire anything — it just works.

data-mode="inline" is the default; you can omit it.


Render a clickable trigger that opens the survey in a centered modal:

html
<button data-krafter-survey="product-feedback" data-mode="popup">
  Give us feedback
</button>

<script src="https://app.krafter.dev/embed/surveys.js" defer></script>

Clicking the element opens a full-screen overlay with the survey iframe inside a centered modal (max-width 640px, max-height 90vh). The overlay closes when:

  • The × button in the corner is clicked
  • The dim background outside the modal is clicked

If you want a different element to be the trigger (for example, a div containing your own button styling), nest a data-trigger element inside:

html
<div data-krafter-survey="product-feedback" data-mode="popup">
  <button data-trigger>Give us feedback</button>
</div>

The whole container becomes clickable by default; data-trigger lets you scope the click handler to a specific child.


Slide-in / other modes

Only inline and popup are wired up today. data-mode="slide-in" is recognised in the script but currently falls through to the no-op branch — treat it as reserved.


Customising the survey UI

The embed script is intentionally thin — it doesn't know about colors, fonts, or button labels. Survey appearance is controlled by survey.settings, which the public /s/:slug page reads. To customise:

  1. Edit settings on the survey via PATCH /surveys/:id. Schema today is free-form (theme, button labels, redirect URL); the dashboard provides the canonical authoring UI
  2. Or use the Surveys dashboard "Appearance" tab on each survey

The same settings apply to:

  • The standalone public page at https://app.krafter.dev/s/:slug
  • The inline embed
  • The popup embed
  • Per-respondent invite links at /s/:slug/:token

Multiple surveys per page

The script scans for all data-krafter-survey containers on DOMContentLoaded. Multiple inline embeds, multiple popups, or a mix — drop as many as you need:

html
<section>
  <h2>Quick poll</h2>
  <div data-krafter-survey="weekly-pulse"></div>
</section>

<aside>
  <button data-krafter-survey="bug-report" data-mode="popup">Report an issue</button>
</aside>

Each container is independent — they don't share state and they don't need to be siblings.


Adding extra context to responses

The embed script doesn't expose hooks to inject metadata or respondent_token today. If you need to attach custom metadata (UTM tags, internal user id) or link a response back to an invited respondent, don't use the embed script — render the survey yourself by calling the public response API and passing those fields on start_response. The embed widget is the one-line drop-in path; the API is the full-control path.

For the per-respondent invitation flow (token-based), see Respondents.


Content Security Policy

Hosts running with a strict CSP need to allow:

  • script-src https://app.krafter.dev — to load the embed script
  • frame-src https://app.krafter.dev — to render the iframe
  • img-src https://app.krafter.dev — for any survey-side imagery

If you self-host, swap app.krafter.dev for your own host.


Caveats

  • iframes block password manager autofill for email questions on some browsers. If your survey starts with email capture and you depend on autofill, prefer a per-respondent invite link instead of the embed
  • Mobile keyboards can cover the inline iframe if it sits at the bottom of a tall page. The auto-resize logic responds to question changes but not to viewport changes — design the surrounding page so the iframe has room to grow
  • No script-level events fired today — there is no krafter:survey:completed event on window to listen to. Use the response.completed webhook instead, or poll the response endpoint server-side

Built by Krafter Studio