Developer resources

The Makeform embed SDK lets you control embedded forms, popups, and sliders with JavaScript for free on every plan. Get your form embed from Share > Embed Form, then use the API methods below.

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.


Including the SDK

Add the embed script to your page:

HTML
<script async src="https://www.makeform.ai/widgets/embed.js"></script>

The script exposes the global MakeForm object. It can be placed in the <head> or before </body>. It initializes existing data-makeform-src, data-makeform-popup, data-makeform-slider, and auto-open configuration when the DOM is ready.


API methods

MakeForm.loadEmbeds()

Scans the DOM for elements with data-makeform-src and initializes them as embedded forms.

JavaScript
MakeForm.loadEmbeds();

Call this after dynamically adding embed elements to the page (e.g., after SPA navigation).

MakeForm.loadPopups()

Scans for elements with data-makeform-popup and attaches click handlers.

JavaScript
MakeForm.loadPopups();

MakeForm.loadSliders()

Scans for elements with data-makeform-slider and attaches click handlers.

JavaScript
MakeForm.loadSliders();

MakeForm.loadAutoOpen()

Processes window.MakeFormConfig and window.MakeFormConfigs to set up auto-open triggers.

JavaScript
MakeForm.loadAutoOpen();

MakeForm.openPopup(formId, options?)

Opens a popup modal programmatically.

JavaScript
MakeForm.openPopup("YOUR_FORM_ID", {
  width: 500,
  position: "bottom-right",
  overlay: false,
  initialBgColor: "#ffffff",
  hiddenFields: { user_id: "123" },
  dispatchEvents: true,
  autoCloseMs: 3000,
  onOpen: ({ formId }) => console.log("Opened:", formId),
  onReady: ({ formId }) => console.log("Ready:", formId),
  onSubmit: ({ formId, data }) => console.log("Submitted:", data),
  onClose: ({ formId }) => console.log("Closed:", formId)
});

MakeForm.closePopup()

Closes the currently open popup.

JavaScript
MakeForm.closePopup();

MakeForm.openSlider(formId, options?)

Opens a slider panel programmatically. Accepts the same options as openPopup, plus a position option for slider side.

JavaScript
MakeForm.openSlider("YOUR_FORM_ID", {
  width: 400,
  position: "left",
  onSubmit: ({ formId, data }) => {
    console.log("Feedback received:", data);
  }
});

MakeForm.closeSlider()

Closes the currently open slider.

JavaScript
MakeForm.closeSlider();

MakeForm.registerAutoOpen(config)

Registers a single auto-open configuration at runtime. Useful for dynamically adding forms after the SDK has loaded.

JavaScript
MakeForm.registerAutoOpen({
  formId: "YOUR_FORM_ID",
  popup: {
    width: 500,
    open: { trigger: "time", ms: 5000 }
  }
});

ModalOptions

Options object for openPopup() and openSlider():

TypeScript
interface ModalOptions {
  type?: 'embed' | 'form';       // 'embed' uses /e/ route, 'form' uses /f/ route
  embedUrl?: string;              // Pre-built embed URL; preserves its origin
  hiddenFields?: Record<string, string | number | boolean | null>;
  width?: number;                 // Modal width in px (360–800)
  position?: string;              // Popup: 'center' | 'bottom-right'; Slider: 'left' | 'right'
  overlay?: boolean;              // Show dark backdrop (popup only)
  initialBgColor?: string;        // CSS color to prevent white flash
  onOpen?: (payload: { formId: string }) => void;
  onReady?: (payload: { formId: string }) => void;
  onSubmit?: (payload: { formId: string; data: Record<string, any> }) => void;
  onClose?: (payload: { formId: string }) => void;
  dispatchEvents?: boolean;       // Dispatch CustomEvents on window
  autoCloseMs?: number | null;    // Auto-close delay in ms (0–30000)
}
Option Type Description Default
type 'embed' | 'form' Route type: embed = /e/, form = /f/ 'embed'
embedUrl string Pre-built embed URL. Useful when you need to preserve a generated URL or custom origin —
hiddenFields object Key-value pairs passed as URL parameters —
width number Modal width in pixels, clamped to 360–800 Popup CSS default, slider 500
position string Popup: 'center' or 'bottom-right'. Slider: 'left' or 'right' Popup: 'center', Slider: 'right'
overlay boolean Show dark backdrop behind popup. Center always shows overlay; bottom-right defaults to off Popup center: true, bottom-right: false
initialBgColor string Initial background color, if valid CSS color —
onOpen function Called when the modal opens —
onReady function Called when the form loads inside the modal —
onSubmit function Called on successful submission, receives form data —
onClose function Called when the modal closes —
dispatchEvents boolean Enable CustomEvent dispatching on window false
autoCloseMs number | null Auto-close delay after submit (0–30000ms) null

Auto-open configuration

window.MakeFormConfig

Configure a single auto-open form:

TypeScript
interface MakeFormConfig {
  formId: string;                 // Form ID (8–15 alphanumeric chars + hyphens)
  embedUrl?: string;              // Optional pre-built embed URL
  popup?: AutoOpenEntry;          // Popup auto-open config
  slider?: AutoOpenEntry;         // Slider auto-open config
}

interface AutoOpenEntry {
  width?: number;                 // Modal width (360–800)
  position?: string;              // Popup: 'center' | 'bottom-right'; Slider: 'left' | 'right'
  overlay?: boolean;              // Show dark backdrop (popup only)
  showOnce?: boolean;             // Show only once per visitor
  doNotShowAfterSubmit?: boolean; // Don't show after submission
  autoClose?: number;             // Auto-close delay in ms (0–30000)
  hiddenFields?: Record<string, string | number | boolean>;
  dispatchEvents?: boolean;       // Enable CustomEvents
  bgColor?: string;               // Initial background color
  open?: {                        // Trigger config (omit for page load)
    trigger: 'time' | 'exit' | 'scroll';
    ms?: number;                  // For 'time': delay in ms (500–120000)
    scrollPercent?: number;       // For 'scroll': percentage (1–100)
  };
}

window.MakeFormConfigs

Array of configurations for multiple forms (max 20):

JavaScript
window.MakeFormConfigs = [
  {
    formId: "FORM_1",
    popup: {
      width: 500,
      showOnce: true,
      open: { trigger: "time", ms: 5000 }
    }
  },
  {
    formId: "FORM_2",
    slider: {
      width: 400,
      doNotShowAfterSubmit: true,
      open: { trigger: "scroll", scrollPercent: 75 }
    }
  }
];

Framework examples

React / Next.js

EmbeddedForm.tsx
'use client';

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

export default function EmbeddedForm({ formId }: { formId: string }) {
  const containerRef = useRef<HTMLDivElement>(null);

  const handleScriptLoad = () => {
    if (typeof MakeForm !== 'undefined') {
      MakeForm.loadEmbeds();
    }
  };

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

Opening a popup programmatically in React:

PopupButton.tsx
'use client';

export default function PopupButton({ formId }: { formId: string }) {
  const openForm = () => {
    if (typeof MakeForm !== 'undefined') {
      MakeForm.openPopup(formId, {
        width: 500,
        onSubmit: ({ data }) => {
          console.log('Submitted:', data);
        }
      });
    }
  };

  return <button onClick={openForm}>Open Form</button>;
}

Vue

EmbeddedForm.vue
<template>
  <div>
    <iframe
      :data-makeform-src="`https://www.makeform.ai/e/${formId}`"
      data-makeform-auto-resize
      data-makeform-events
      loading="lazy"
      :style="{ width: '100%', height: '200px', border: 0 }"
      title="Makeform form"
    />
  </div>
</template>

<script setup>
import { onMounted, defineProps } from 'vue';

const props = defineProps({ formId: String });

onMounted(() => {
  const script = document.createElement('script');
  script.src = 'https://www.makeform.ai/widgets/embed.js';
  script.onload = () => {
    if (typeof MakeForm !== 'undefined') {
      MakeForm.loadEmbeds();
    }
  };
  document.body.appendChild(script);
});
</script>

Opening a popup in Vue:

PopupButton.vue
<template>
  <button @click="openForm">Open Form</button>
</template>

<script setup>
const props = defineProps({ formId: String });

const openForm = () => {
  if (typeof MakeForm !== 'undefined') {
    MakeForm.openPopup(props.formId, {
      width: 500,
      onSubmit: ({ data }) => console.log('Submitted:', data)
    });
  }
};
</script>

Vanilla JS

index.html
<!DOCTYPE html>
<html>
<head>
  <title>My Form</title>
</head>
<body>
  
  <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="Contact Form"
  ></iframe>

  
  <button
    data-makeform-popup="https://www.makeform.ai/e/YOUR_FORM_ID"
    data-makeform-width="500"
    data-makeform-events
  >
    Open Popup
  </button>

  
  <button
    data-makeform-slider="https://www.makeform.ai/e/YOUR_FORM_ID"
    data-makeform-width="400"
  >
    Open Slider
  </button>

  <script async src="https://www.makeform.ai/widgets/embed.js"></script>
</body>
</html>

Programmatic popup with vanilla JS:

app.js
document.getElementById('my-button').addEventListener('click', () => {
  MakeForm.openPopup('YOUR_FORM_ID', {
    width: 500,
    hiddenFields: { source: 'landing-page' },
    onSubmit: ({ data }) => {
      alert('Thank you for your submission!');
      console.log(data);
    }
  });
});

SPA considerations

In single-page applications, the SDK initializes on first page load but won't automatically detect new embed elements added during client-side navigation.

After adding new embeds dynamically, call the appropriate load method:

JavaScript
// After navigating to a page with an inline embed
MakeForm.loadEmbeds();

// After navigating to a page with popup triggers
MakeForm.loadPopups();

// After navigating to a page with slider triggers
MakeForm.loadSliders();

// After adding or changing window.MakeFormConfig / window.MakeFormConfigs
MakeForm.loadAutoOpen();

React example — reload embeds on route change:

TSX
import { useEffect } from 'react';
import { usePathname } from 'next/navigation';

export function MakeFormLoader() {
  const pathname = usePathname();

  useEffect(() => {
    if (typeof MakeForm !== 'undefined') {
      MakeForm.loadEmbeds();
      MakeForm.loadPopups();
      MakeForm.loadSliders();
      MakeForm.loadAutoOpen();
    }
  }, [pathname]);

  return null;
}

Event quick reference

For detailed event documentation, see Handle Form Events.

PostMessage events

Event Description
MakeForm.Loaded Form iframe loaded
MakeForm.SubmitSuccess Form submitted successfully
MakeForm.SetHeight Content height changed (internal)
MakeForm.LayoutReady First stable layout height is ready (internal)
MakeForm.SetBackgroundColor Background color determined (internal)
MakeForm.Submitted Payload-free submit signal for safe auto-close (internal)

Custom DOM events

Event Detail Fires when
MakeForm:open { formId } Popup/slider opens
MakeForm:ready { formId } Form iframe reports loaded
MakeForm:submit { formId, data } Form submitted
MakeForm:close { formId } Popup/slider closes

URL parameters

Parameter Description
mk-rm-bg=true Remove form background (transparent)
mk-align-left=1 Align supported embedded form content to the left
mf_locale={locale} Request a runtime locale for system copy and paid translation overlays
__mf_emitEvents=1 Enable postMessage events on full page (/f/) route
{fieldName}={value} Set hidden field values

Troubleshooting

Form not loading

  • Verify the form ID is correct and the form is published
  • Check that embed.js is loaded (look for the script in the Network tab)
  • Look for errors in the browser console
  • Ensure your content security policy allows iframes from makeform.ai

Height not adjusting

  • Add data-makeform-auto-resize to the iframe element
  • Use the /e/{formId} route for inline embeds
  • The SDK uses ResizeObserver to detect content changes automatically
  • If content is loaded asynchronously inside the form, height updates when the content renders

Events not firing

  • For inline embeds: add data-makeform-events to the iframe
  • For popups/sliders: add data-makeform-events to the trigger or set dispatchEvents: true
  • For full page embeds: append __mf_emitEvents=1 to the URL
  • Listen for MakeForm:* CustomEvents unless you specifically need raw postMessage payloads

Popup/slider not opening

  • Ensure the form ID matches the pattern: 8-15 alphanumeric characters and hyphens
  • Check that embed.js has loaded before calling MakeForm.openPopup()
  • On SPAs, call MakeForm.loadPopups() after adding trigger elements
  • For auto-open snippets, call MakeForm.loadAutoOpen() after changing window.MakeFormConfig