Embed on your website
Render your form inline on any web page with two small options: the script loader or a plain iframe. Both support every major framework. For code-level integration, the eesyform-embed npm package ships typed React, Vue and vanilla bindings.
Auto-resizing, ffSubmit events, framework-agnostic.
Inline, popup, slider, side tab and full-page launch styles.
IframeA single URL that works everywhere, no JavaScript required.
SharingShareable link, QR code, social sharing and OG preview cards.
1Enable embedding
In the builder, open Share & embed and turn on Allow embedding. The embed URL only serves published forms, so make sure your form is published first.
2The script loader
The loader finds every data-ff-embed container on the page and mounts an auto-resizing iframe inside it. Include the script tag exactly once per page, even if you have several forms.
<!doctype html>
<html>
<head>
<title>Contact</title>
</head>
<body>
<h1>Get in touch</h1>
<!-- 1. The container EesyForm fills in -->
<div data-ff-embed data-ff-form="my-form-abc123"></div>
<!-- 2. The loader (one tag per page) -->
<script async src="https://YOUR-EESYFORM-HOST.com/embed.js"></script>
</body>
</html>Options & events
Configure each embed with data attributes on the container:
data-ff-host— override the EesyForm host (auto-detected from the script tag by default)data-ff-height— initial height in pixels before the form measures itselfdata-ff-mode—inline(default),popup,slider,side-taborfull-pagedata-ff-trigger— label for the launch button used by non-inline modesdata-ff-width— panel width in pixels for slider and side-tab modes (default 380)data-ff-position—right,leftorbottomedge for the slider and side-tab modes
<!-- Optional data attributes -->
<div
data-ff-embed
data-ff-form="my-form-abc123"
<!-- Force a different EesyForm host -->
data-ff-host="https://forms.mycompany.com"
<!-- Initial height before the form measures itself -->
data-ff-height="480"
<!-- Launch style: inline (default), popup, slider, side-tab, full-page -->
data-ff-mode="slider"
<!-- Trigger button label for non-inline modes -->
data-ff-trigger="Open form"
<!-- Edge for slider / side-tab: right (default), left, bottom -->
data-ff-position="right"
></div>
<script async src="https://YOUR-EESYFORM-HOST.com/embed.js"></script><!-- Listen for a completed submission (detail is { type: "ff:submit" }) -->
<script>
document.addEventListener("ffSubmit", (event) => {
console.log("Form completed!", event.detail);
});
</script>
<!-- ...or the global callback (called with the container element) -->
<script>
window.ffSubmit = function (container, detail) {
console.log("Completed in", container, detail);
};
</script>3Embed modes
Beyond inline, the script loader supports four launch styles. Pick a mode with data-ff-mode — the loader builds the trigger button and panel for you.
- Popup — a button opens the form in a centered modal with an overlay.
- Slider — a button slides a panel in from the right, left or bottom edge.
- Side tab — a slim tab pinned to the page edge opens a panel when clicked.
- Full page — the form covers the entire viewport.
<div data-ff-embed data-ff-form="my-form-abc123"
data-ff-mode="popup"
data-ff-trigger="Open feedback form"></div>
<script async src="https://YOUR-EESYFORM-HOST.com/embed.js"></script><div data-ff-embed data-ff-form="my-form-abc123"
data-ff-mode="side-tab"
data-ff-position="left"
data-ff-trigger="Leave feedback"></div>
<script async src="https://YOUR-EESYFORM-HOST.com/embed.js"></script>4Sharing & social preview
When the link is shared, social platforms render anOpen Graph preview card with the form's title and description. The preview image is the default EesyForm card (the image is static; per-form themed images are not generated). If you use a custom domain, the preview URL reflects it, but the image stays the default.
5The iframe embed
No script? Just drop the iframe anywhere. The embed page resizes itself and reports its height back, but a fixed height is used until the first measurement arrives.
<iframe
src="https://YOUR-EESYFORM-HOST.com/r/my-form-abc123/embed"
width="100%"
height="640"
frameborder="0"
style="border: 0; display: block; max-width: 100%"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; payment"
loading="lazy"
></iframe>6Framework recipes
For app code, install the package and use its bindings — no manual script injection needed.
npm install eesyform-embedVanilla JS
Mount a form imperatively with embedForm, or keep the declarative data-ff-embed markup and scan it with mountAll.
import { embedForm } from "eesyform-embed";
const handle = embedForm(document.getElementById("form"), {
form: "my-form-abc123",
host: "https://forms.example.com",
height: 480,
mode: "popup", // inline | popup | slider | side-tab | full-page
trigger: "Open form", // label for non-inline modes
position: "right", // right | left | bottom (slider / side-tab)
onSubmit: (detail) => console.log("Completed", detail),
});
// later
handle.unmount();import { mountAll } from "eesyform-embed";
mountAll();React (18 / 19)
import { EesyForm } from "eesyform-embed/react";
export default function ContactPage() {
return (
<EesyForm
form="my-form-abc123"
host="https://forms.example.com"
onSubmit={(detail) => console.log("Completed", detail)}
/>
);
}import { useEesyForm } from "eesyform-embed/react";
function ContactPage() {
const { ref, submit } = useEesyForm({ form: "my-form-abc123" });
return <div ref={ref} />;
}Angular
EesyForm ships the framework-agnostic core; Angular directives are compiled by your own app, so add a tiny standalone directive that calls embedForm.
import { Directive, ElementRef, Input, OnDestroy, OnInit } from "@angular/core";
import { embedForm, type EmbedHandle } from "eesyform-embed";
@Directive({
selector: "[ffForm]",
standalone: true,
})
export class EesyFormDirective implements OnInit, OnDestroy {
@Input("ffForm") form = "";
@Input() ffHost?: string;
@Input() ffHeight?: number;
private handle?: EmbedHandle;
constructor(private el: ElementRef<HTMLElement>) {}
ngOnInit() {
this.handle = embedForm(this.el.nativeElement, {
form: this.form,
host: this.ffHost,
height: this.ffHeight,
});
}
ngOnDestroy() {
this.handle?.unmount();
}
}<div ffForm="my-form-abc123" ffHost="https://forms.example.com"></div>Vue 3
<script setup>
import { EesyForm } from "eesyform-embed/vue";
</script>
<template>
<EesyForm form="my-form-abc123" host="https://forms.example.com" />
</template>Next.js (App Router)
The React binding is a client component, so it works directly in App Router pages — including server components — with no extra setup.
import { EesyForm } from "eesyform-embed/react";
export default function ContactPage() {
return (
<main>
<h1>Contact us</h1>
<EesyForm form="my-form-abc123" host="https://forms.example.com" />
</main>
);
}