Framer guide
Framer's built-in Form component covers the basics, but the submissions live inside Framer. If you want each one emailed to you and stored in a dashboard you control — with AI spam scoring, Slack/Discord forwarding, webhooks, and CSV export — drop a plain HTML form into a Framer Embed element that POSTs to FormLoom. It's markup only: no scripts to load, nothing to maintain, and the exact same form keeps working if you ever move your site off Framer.
Approach
Add an Embed element to your Framer page, choose HTML, and paste a `<form method="POST">` block pointing at FormLoom. Include a small `<style>` block in the same embed so the form matches your site's design.
Code
contact.html
<form action="https://formloom.vercel.app/api/submit/YOUR_ACCESS_KEY" method="POST">
<label>
Name
<input type="text" name="name" required />
</label>
<label>
Email
<input type="email" name="email" required />
</label>
<label>
Message
<textarea name="message" required placeholder="How can we help?"></textarea>
</label>
<!-- honeypot: bots fill this, humans don't see it -->
<input type="checkbox" name="botcheck" style="display:none" tabindex="-1" autocomplete="off" />
<button type="submit">Send</button>
</form>contact.html
<form id="contact-form">
<label>
Name
<input type="text" name="name" required />
</label>
<label>
Email
<input type="email" name="email" required />
</label>
<label>
Message
<textarea name="message" required placeholder="How can we help?"></textarea>
</label>
<!-- honeypot: bots fill this, humans don't see it -->
<input type="checkbox" name="botcheck" style="display:none" tabindex="-1" autocomplete="off" />
<button type="submit">Send</button>
</form>
<script>
const form = document.getElementById("contact-form");
form.addEventListener("submit", async (e) => {
e.preventDefault();
const res = await fetch("https://formloom.vercel.app/api/submit/YOUR_ACCESS_KEY", {
method: "POST",
headers: { "Content-Type": "application/json", Accept: "application/json" },
body: JSON.stringify(Object.fromEntries(new FormData(form))),
});
const data = await res.json();
if (data.success) {
form.reset();
alert("Thanks — we got your message.");
} else {
alert(data.message || "Something went wrong.");
}
});
</script>submit.ts
// npm i @formloom/client
import { createForm } from "@formloom/client";
type Contact = {
name: string;
email: string;
message: string;
};
const form = createForm<Contact>("YOUR_ACCESS_KEY");
// End-to-end typed: TS errors if you send the wrong shape.
const { success, message } = await form.submit({
name: form.name.value,
email: form.email.value,
message: form.message.value,
});Gotchas
- Embeds can render as a placeholder on the editor canvas — always test the form on the site preview or the published URL, not the canvas.
- Framer's design styles don't cascade into markup inside an Embed; ship a `<style>` block (or inline styles) with the form so it doesn't render as unstyled browser defaults.
- Add a hidden `redirect` input to send visitors to your own Framer thank-you page after submit — otherwise they land on FormLoom's default success page.
- Framer's native Form component submits to Framer, not to FormLoom — use the Embed variant when you want the submission in your FormLoom dashboard.
FAQ
Native Framer forms keep submissions inside Framer. FormLoom emails you every submission, stores it permanently in a dashboard with CSV export, spam-scores it with AI, and can forward it to Slack, Discord, or any webhook — and because the form is plain HTML, it comes with you if you ever leave Framer.
See the full Framer contact form page with a live demo.