chatformdocs

Embedding

A script tag or an iframe. No backend, no keys.

The fastest way to put a form somewhere is not to use the API at all.

<script
  src="https://chatform.in/embed.js"
  data-form="your-form-slug"
  data-mode="popup"
  defer
></script>

data-mode takes popup, side-tab, inline or fullpage.

The loader injects the iframe lazily — on intent, not on load — so it does not compete with your own page for the first paint.

Where it sits, and what it looks like

<script
  src="https://chatform.in/embed.js"
  data-form="your-form-slug"
  data-position="bottom-left"
  data-offset="32"
  data-button-color="#0ea5e9"
  data-label="Talk to us"
  data-width="420"
  data-height="640"
  defer
></script>
AttributeTakesDefault
data-positionbottom-right, bottom-left, top-right, top-leftbottom-right
data-offsetpx between the launcher and the edges20
data-widthpanel width in px400 (440 for a side tab)
data-heightpanel height in px; inline takes auto600
data-button-colorlauncher colour (data-color still works)#FD6F29
data-labellauncher text; "" for an icon-only bubbleFill this form
data-iconchat or nonechat
data-launchernone hides the corner buttonshown
data-themelight, dark, autoauto
data-open-onclick, load, exit-intent, scroll:<pct>click

Below 520px wide the panel goes full screen whatever you set, because a 400px panel inset from the corner of a phone is a form nobody can fill in.

Two forms can sit on one page in two different corners — each script tag gets its own placement rules, and window.Chatform.get("<slug>") reaches each one.

Inline

<iframe
  src="https://chatform.in/f/your-form-slug?embed=1"
  width="100%"
  height="640"
  style="border:0"
></iframe>

?embed=1 renders the form without the standalone page's chrome.

Prefilling

Anything you already know, pass in — the respondent is not asked for it twice.

<script
  src="https://chatform.in/embed.js"
  data-form="your-form-slug"
  data-hidden-plan="trial"
  data-hidden-utm_source="pricing-page"
  defer
></script>

Or as query parameters on an iframe: ?plan=trial&utm_source=pricing-page. Only the hidden fields the form declares are accepted; anything else is ignored.

Reacting to it

The frame posts messages to your page:

window.addEventListener("message", (event) => {
  if (event.origin !== "https://chatform.in") return;
  const message = event.data;
  if (message?.source !== "chatform") return;

  switch (message.type) {
    case "ready":    break;
    case "resize":   /* message.height */ break;
    case "question": /* message.ref */ break;
    case "answer":   /* message.ref — the value is deliberately not included */ break;
    case "complete": analytics.track("form_completed", { id: message.responseId }); break;
    case "close":    break;
  }
});

answer carries the question's ref and type, never the value. Putting a respondent's answers into the embedding page's JavaScript by default is not a default anyone asked for — use webhooks or the responses API if you need the data.

Always check event.origin. Any page can post a message to yours.

Opening it from your own button

Add data-chatform-open to any button or link on the page and clicking it opens the form. It works alongside the corner button, or on its own with data-launcher="none".

<button type="button" data-chatform-open>Join the waitlist</button>

With two forms on one page, name the one it opens: data-chatform-open="your-form-slug".

Controlling it

window.Chatform.open();
window.Chatform.close();
window.Chatform.toggle();
window.Chatform.prefill({ plan: "pro" });
window.Chatform.on("complete", (e) => console.log(e.responseId));

Calls made before the script loads are queued and replayed, so you do not have to wait for it.

Restricting where a form can be framed

A form can list the origins allowed to embed it. The allowlist is enforced when a session is opened — a page that is not on it cannot start one, whatever it does in the browser — and it also produces a frame-ancestors policy so the browser refuses the frame in the first place.

Leave it empty and the form embeds anywhere, which is usually what a public form wants.

Content Security Policy

If your site sets a CSP, the embed needs:

frame-src https://chatform.in;
script-src https://chatform.in;

The loader adds no inline script and evaluates nothing. If your policy uses nonces, put yours on the script tag as data-nonce and it will be copied onto the styles it injects.

On this page