# Handle form events

Makeform embed events let you track form readiness, submissions, and popup or slider opening and closing for free on every plan. Get your form embed from **Share** > **Embed Form**, then enable events in your website code.

Makeform is a free AI form builder: describe what you want to collect and it builds the form for you, with unlimited forms and submissions on the free plan.

:::callout{type="success"}
Embed events are available for **free** to all Makeform users.
:::

---

## Event layers

Makeform uses two event layers:

| Layer              | Who uses it                         | How to listen                                         |
| ------------------ | ----------------------------------- | ----------------------------------------------------- |
| SDK CustomEvents   | Most website analytics and app code | `window.addEventListener('MakeForm:submit', handler)` |
| iframe postMessage | Advanced integrations and debugging | `window.addEventListener('message', handler)`         |

For most websites, use SDK CustomEvents. The SDK validates the iframe source and converts Makeform iframe messages into simpler `MakeForm:*` events when you opt in.

---

## Enable events

### Standard embed

Add **data-makeform-events** to the iframe:

```html copy
<iframe
  data-makeform-src="https://www.makeform.ai/e/YOUR_FORM_ID"
  data-makeform-auto-resize
  data-makeform-events
  loading="lazy"
  style="width: 100%; height: 200px; border: 0;"
  title="Makeform form"
></iframe>
<script async src="https://www.makeform.ai/widgets/embed.js"></script>
```

Standard embeds dispatch `MakeForm:ready` and `MakeForm:submit`.

### Popup and slider

For data-attribute triggers, add **data-makeform-events** to the trigger:

```html copy
<button
  data-makeform-popup="https://www.makeform.ai/e/YOUR_FORM_ID"
  data-makeform-events
>
  Open Form
</button>
<script async src="https://www.makeform.ai/widgets/embed.js"></script>
```

For the JavaScript API, set `dispatchEvents: true`:

```javascript copy
MakeForm.openPopup("YOUR_FORM_ID", {
  dispatchEvents: true
});
```

Popup and slider embeds can dispatch `MakeForm:open`, `MakeForm:ready`, `MakeForm:submit`, and `MakeForm:close`.

### Full page embed

Full page embeds use `/f/{FORM_ID}`. To emit submission events, append `__mf_emitEvents=1`, use `data-makeform-src`, and add `data-makeform-events`:

```html copy
<iframe
  data-makeform-src="https://www.makeform.ai/f/YOUR_FORM_ID?__mf_emitEvents=1"
  data-makeform-events
  style="position: absolute; inset: 0; border: 0; width: 100%; height: 100%;"
  title="Makeform form"
></iframe>
<script async src="https://www.makeform.ai/widgets/embed.js"></script>
```

---

## SDK CustomEvents

### MakeForm\:ready

Fires when the form iframe reports that it has loaded.

```javascript copy
window.addEventListener("MakeForm:ready", (event) => {
  console.log("Ready:", event.detail.formId);
});
```

### MakeForm\:submit

Fires when the form is successfully submitted.

```javascript copy
window.addEventListener("MakeForm:submit", (event) => {
  console.log("Submitted form:", event.detail.formId);
  console.log("Submission data:", event.detail.data);
});
```

### MakeForm\:open

Fires when a popup or slider opens.

```javascript copy
window.addEventListener("MakeForm:open", (event) => {
  console.log("Opened:", event.detail.formId);
});
```

### MakeForm\:close

Fires when a popup or slider closes.

```javascript copy
window.addEventListener("MakeForm:close", (event) => {
  console.log("Closed:", event.detail.formId);
});
```

---

## PostMessage events

The form iframe sends `postMessage` payloads to its parent window. These are useful if you are building a custom wrapper around the embed SDK.

### MakeForm.Loaded

```json
{
  "type": "MakeForm.Loaded",
  "payload": {}
}
```

### MakeForm.SubmitSuccess

```json
{
  "type": "MakeForm.SubmitSuccess",
  "payload": {
    "Name": "Jane Doe",
    "Email": "jane@example.com"
  }
}
```

### Internal layout messages

The SDK also listens for these iframe messages:

| Event                         | Purpose                                                                            |
| ----------------------------- | ---------------------------------------------------------------------------------- |
| `MakeForm.SetHeight`          | Auto-resizes standard embeds and popup height. Payload shape: `{ "height": 640 }`. |
| `MakeForm.LayoutReady`        | Reveals popups only after the first stable layout height is available.             |
| `MakeForm.SetBackgroundColor` | Syncs modal background color with the form background.                             |
| `MakeForm.Submitted`          | Payload-free submit signal used for safe auto-close on `/f` popup embeds.          |

:::callout{type="info"}
Treat layout messages as SDK internals. For analytics and product logic, prefer `MakeForm:ready`, `MakeForm:submit`, `MakeForm:open`, and `MakeForm:close`.
:::

---

## Complete examples

:::code-tabs
```javascript filename="events.js" copy
window.addEventListener("MakeForm:ready", (event) => {
  console.log("Form ready:", event.detail.formId);
});

window.addEventListener("MakeForm:submit", (event) => {
  console.log("Submitted:", event.detail.formId, event.detail.data);
});

window.addEventListener("MakeForm:open", (event) => {
  console.log("Modal opened:", event.detail.formId);
});

window.addEventListener("MakeForm:close", (event) => {
  console.log("Modal closed:", event.detail.formId);
});
```

```typescript filename="events.ts" copy
type MakeFormEventDetail = {
  formId: string;
  data?: Record<string, unknown>;
};

const onSubmit = (event: Event) => {
  const detail = (event as CustomEvent<MakeFormEventDetail>).detail;
  console.log("Submitted:", detail.formId, detail.data);
};

window.addEventListener("MakeForm:submit", onSubmit);
```

```tsx filename="EmbeddedForm.tsx" copy
'use client';

import Script from 'next/script';
import { useEffect } from 'react';

export function EmbeddedForm({ formId }: { formId: string }) {
  useEffect(() => {
    const handleSubmit = (event: Event) => {
      const detail = (event as CustomEvent).detail;
      console.log('Submitted:', detail.formId, detail.data);
    };

    window.addEventListener('MakeForm:submit', handleSubmit);
    return () => window.removeEventListener('MakeForm:submit', handleSubmit);
  }, []);

  return (
    <>
      <iframe
        data-makeform-src={`https://www.makeform.ai/e/${formId}`}
        data-makeform-auto-resize
        data-makeform-events
        loading="lazy"
        style={{ width: '100%', height: 200, border: 0 }}
        title="Makeform form"
      />
      <Script
        async
        src="https://www.makeform.ai/widgets/embed.js"
        onLoad={() => window.MakeForm?.loadEmbeds?.()}
      />
    </>
  );
}
```
:::

---

## Advanced postMessage listener

If you listen to raw iframe messages, validate the origin before using the payload:

```javascript copy
window.addEventListener("message", (event) => {
  if (
    event.origin !== "https://www.makeform.ai" &&
    event.origin !== "https://makeform.ai"
  ) {
    return;
  }

  if (typeof event.data !== "string") return;

  try {
    const data = JSON.parse(event.data);

    if (data.type === "MakeForm.SubmitSuccess") {
      console.log("Submission:", data.payload);
    }
  } catch {
    // Ignore unrelated postMessage traffic.
  }
});
```

---

## Event reference

### SDK CustomEvents

| Event             | Fires when                 | Detail             |
| ----------------- | -------------------------- | ------------------ |
| `MakeForm:ready`  | Form iframe reports loaded | `{ formId }`       |
| `MakeForm:submit` | Form submission succeeds   | `{ formId, data }` |
| `MakeForm:open`   | Popup or slider opens      | `{ formId }`       |
| `MakeForm:close`  | Popup or slider closes     | `{ formId }`       |

### iframe postMessage events

| Event                         | Public use                                   |
| ----------------------------- | -------------------------------------------- |
| `MakeForm.Loaded`             | Advanced wrapper can detect iframe readiness |
| `MakeForm.SubmitSuccess`      | Advanced wrapper can read submission data    |
| `MakeForm.SetHeight`          | Internal resizing                            |
| `MakeForm.LayoutReady`        | Internal popup reveal timing                 |
| `MakeForm.SetBackgroundColor` | Internal modal background sync               |
| `MakeForm.Submitted`          | Internal payload-free auto-close signal      |

---

## Best practices

1. **Use SDK CustomEvents first**
   They are easier to consume and work across standard, popup, and slider embeds when event tracking is enabled.

2. **Clean up listeners in SPAs**
   Remove event listeners when components unmount.

3. **Validate postMessage origin**
   Raw `message` listeners receive traffic from many sources. Check `event.origin` before reading the payload.

4. **Do not build product logic on layout messages**
   `SetHeight`, `LayoutReady`, `SetBackgroundColor`, and `Submitted` exist for SDK behavior and can change as the embed runtime evolves.

---

## Related

- [Embed Your Form](/help/guides/embed-your-form) — Overview of all embed types
- [Developer Resources](/help/developer/embedded/developer-resources) — Full JavaScript API reference
