Framer contact form
Verified against Framer web editor · July 2026
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.
The code
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.
<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><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>// 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.
Form types for Framer
Tailored field sets and code for common use cases.