FormLoom
F

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.

Plain HTML (no JavaScript)
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>
AJAX fetch (vanilla JS)
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>
@formloom/client (typed SDK)
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.

Form types for Framer

Tailored field sets and code for common use cases.

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.
Framer contact form — copy-paste code (verified web editor) · FormLoom