Contact form you can copy
The sample is the short version on purpose. Every extra field is one more reason to close the tab. Add a phone or a subject list only if you will use the answer. Type it into the description box, or open “Build it yourself, without AI” and add the row.
The HTML tab is selected. Tailwind, Bootstrap 5, React and Vue are the same three fields in other clothes.
Fields, no AI
Live preview
Preview only. Nothing is sent from this page.
Contact us
Copy the code
Replace fk_your_access_key with your own Formgong access key. The placeholder does not accept submissions. The honeypot field is named botcheck. Leave it empty.
The HTML form posts in the browser. React and Vue show an error on the page if the request fails. These class names need Tailwind on your site. This page does not load Tailwind. Bootstrap 5 class names need Bootstrap on your site. This page does not load Bootstrap.
<style>
#fg-fk_your_access_key{box-sizing:border-box;max-width:520px;width:100%;padding:28px;border:1px solid #dbe2ea;border-radius:8px;background:#fff;color:#0f172a;font:16px/1.5 system-ui,sans-serif;display:grid;gap:16px}#fg-fk_your_access_key *{box-sizing:border-box}#fg-fk_your_access_key h2{font-size:24px;line-height:1.3;margin:0 0 4px;color:#0f172a}#fg-fk_your_access_key label{display:grid;gap:6px;font-weight:500}#fg-fk_your_access_key input,#fg-fk_your_access_key textarea,#fg-fk_your_access_key select{width:100%;font:inherit;min-height:44px;border:1px solid #94a3b8;border-radius:8px;padding:10px 12px;background:#fff;color:#0f172a}#fg-fk_your_access_key textarea{min-height:96px;resize:vertical}#fg-fk_your_access_key .fg-reminder-consent{display:flex;align-items:flex-start;gap:10px;font-weight:400}#fg-fk_your_access_key input[type=checkbox]{width:20px;min-height:20px;flex-shrink:0;margin:2px 0}#fg-fk_your_access_key button{font:inherit;font-weight:600;min-height:48px;padding:12px 20px;border:0;border-radius:8px;background:#4338ca;color:#ffffff;cursor:pointer}#fg-fk_your_access_key :focus-visible{outline:3px solid #4338ca;outline-offset:3px}#fg-fk_your_access_key button:disabled{cursor:wait;opacity:.7}#fg-fk_your_access_key p{margin:0}#fg-fk_your_access_key fieldset{border:0;padding:0;margin:0;display:grid;gap:8px;min-width:0}#fg-fk_your_access_key legend{font-weight:500;padding:0;margin-bottom:6px}#fg-fk_your_access_key .fg-choice{display:flex;align-items:center;gap:10px;font-weight:400}#fg-fk_your_access_key input[type=radio]{width:20px;min-height:20px;flex-shrink:0;margin:0}#fg-fk_your_access_key .fg-trap{position:absolute;inset-inline-start:0;top:0;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
</style>
<form id="fg-fk_your_access_key" action="https://formgong.com/submit" method="POST">
<h2>Contact us</h2>
<input type="hidden" name="access_key" value="fk_your_access_key">
<input type="hidden" name="_lang" value="en">
<label>Name * <input type="text" name="name" required autocomplete="name"></label>
<label>Email * <input type="email" name="email" required autocomplete="email"></label>
<label>Message * <textarea name="message" required></textarea></label>
<div class="fg-trap" aria-hidden="true"><label>Leave this field empty<input name="botcheck" tabindex="-1" autocomplete="off"></label></div>
<button type="submit">Send message</button>
</form><form class="relative mx-auto grid w-full max-w-lg gap-4 rounded-lg border border-slate-200 bg-white p-6 text-base text-slate-900" action="https://formgong.com/submit" method="POST"> <h2 class="text-2xl font-semibold">Contact us</h2> <input type="hidden" name="access_key" value="fk_your_access_key"> <input type="hidden" name="_lang" value="en"> <div class="grid gap-1"><label class="font-medium" for="tw-name">Name *</label><input class="w-full rounded-md border border-slate-400 bg-white px-3 py-2 text-base text-slate-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-700" id="tw-name" type="text" name="name" autocomplete="name" required></div> <div class="grid gap-1"><label class="font-medium" for="tw-email">Email *</label><input class="w-full rounded-md border border-slate-400 bg-white px-3 py-2 text-base text-slate-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-700" id="tw-email" type="email" name="email" autocomplete="email" required></div> <div class="grid gap-1"><label class="font-medium" for="tw-message">Message *</label><textarea class="w-full rounded-md border border-slate-400 bg-white px-3 py-2 text-base text-slate-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-700 min-h-24" id="tw-message" name="message" required></textarea></div> <div class="absolute start-0 top-0 h-px w-px overflow-hidden" aria-hidden="true"><label>Leave this empty<input name="botcheck" tabindex="-1" autocomplete="off"></label></div> <button class="min-h-12 rounded-md bg-indigo-700 px-4 py-3 font-semibold text-white" type="submit">Send message</button> </form>
<form class="position-relative mx-auto" style="max-width:32rem" action="https://formgong.com/submit" method="POST"> <h2 class="h4 mb-3">Contact us</h2> <input type="hidden" name="access_key" value="fk_your_access_key"> <input type="hidden" name="_lang" value="en"> <div class="mb-3"><label class="form-label" for="bs-name">Name *</label><input class="form-control" id="bs-name" type="text" name="name" autocomplete="name" required></div> <div class="mb-3"><label class="form-label" for="bs-email">Email *</label><input class="form-control" id="bs-email" type="email" name="email" autocomplete="email" required></div> <div class="mb-3"><label class="form-label" for="bs-message">Message *</label><textarea class="form-control" id="bs-message" name="message" rows="4" required></textarea></div> <div class="position-absolute top-0 start-0 overflow-hidden" style="width:1px;height:1px" aria-hidden="true"><label>Leave this empty<input name="botcheck" tabindex="-1" autocomplete="off"></label></div> <button class="btn btn-primary" type="submit">Send message</button> </form>
import { useEffect, useRef, useState } from "react";
// Visitor language for Formgong messages, thank-you text and autoreply.
const LANG = "en";
export default function ContactForm() {
const captcha = useRef(null);
const feedback = useRef(null);
const [failed, setFailed] = useState(false);
const [busy, setBusy] = useState(false);
const [status, setStatus] = useState("");
useEffect(() => mountCaptcha(captcha.current, null), []);
useEffect(() => { if (failed && status) feedback.current?.focus(); }, [failed, status]);
async function onSubmit(event) {
event.preventDefault();
if (busy) return;
const form = event.currentTarget;
const body = new FormData(form);
body.set("access_key", "fk_your_access_key");
body.set("_lang", LANG);
setBusy(true);
setFailed(false);
setStatus("Sending…");
try {
const response = await fetch("https://formgong.com/submit", {
method: "POST", headers: { Accept: "application/json" }, body,
});
const result = await response.json();
setFailed(!result.success);
setStatus(result.success ? (result.message || "Sent. Thank you!") : (result.message || "Could not send. Please try again."));
} catch { setFailed(true); setStatus("Connection failed. Please try again."); }
finally { setBusy(false); resetCaptcha(form); }
}
return <form id="fg-fk_your_access_key" onSubmit={onSubmit} aria-busy={busy}>
<style>{"#fg-fk_your_access_key{box-sizing:border-box;max-width:520px;width:100%;padding:28px;border:1px solid #dbe2ea;border-radius:8px;background:#fff;color:#0f172a;font:16px/1.5 system-ui,sans-serif;display:grid;gap:16px}#fg-fk_your_access_key *{box-sizing:border-box}#fg-fk_your_access_key h2{font-size:24px;line-height:1.3;margin:0 0 4px;color:#0f172a}#fg-fk_your_access_key label{display:grid;gap:6px;font-weight:500}#fg-fk_your_access_key input,#fg-fk_your_access_key textarea,#fg-fk_your_access_key select{width:100%;font:inherit;min-height:44px;border:1px solid #94a3b8;border-radius:8px;padding:10px 12px;background:#fff;color:#0f172a}#fg-fk_your_access_key textarea{min-height:96px;resize:vertical}#fg-fk_your_access_key .fg-reminder-consent{display:flex;align-items:flex-start;gap:10px;font-weight:400}#fg-fk_your_access_key input[type=checkbox]{width:20px;min-height:20px;flex-shrink:0;margin:2px 0}#fg-fk_your_access_key button{font:inherit;font-weight:600;min-height:48px;padding:12px 20px;border:0;border-radius:8px;background:#4338ca;color:#ffffff;cursor:pointer}#fg-fk_your_access_key :focus-visible{outline:3px solid #4338ca;outline-offset:3px}#fg-fk_your_access_key button:disabled{cursor:wait;opacity:.7}#fg-fk_your_access_key p{margin:0}#fg-fk_your_access_key fieldset{border:0;padding:0;margin:0;display:grid;gap:8px;min-width:0}#fg-fk_your_access_key legend{font-weight:500;padding:0;margin-bottom:6px}#fg-fk_your_access_key .fg-choice{display:flex;align-items:center;gap:10px;font-weight:400}#fg-fk_your_access_key input[type=radio]{width:20px;min-height:20px;flex-shrink:0;margin:0}#fg-fk_your_access_key .fg-trap{position:absolute;inset-inline-start:0;top:0;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}"}</style>
<h2>{"Contact us"}</h2>
<label>{"Name"} * <input type="text" name="name" required autoComplete="name" /></label>
<label>{"Email"} * <input type="email" name="email" required autoComplete="email" /></label>
<label>{"Message"} * <textarea name="message" required /></label>
<div aria-hidden="true" style={{position:"absolute",insetInlineStart:0,top:0,width:1,height:1,overflow:"hidden",clipPath:"inset(50%)"}}><input name="botcheck" tabIndex={-1} autoComplete="off" /></div>
<button type="submit" disabled={busy}>{busy ? "Sending…" : "Send message"}</button>
<p ref={feedback} role="status" tabIndex={-1}>{status}</p>
</form>;
}
function mountCaptcha(node, siteKey) {
if (!node || !siteKey) return () => {};
let disposed = false;
let widget;
function render() {
if (disposed || !window.turnstile) return;
window.turnstile.ready(() => {
if (disposed) return;
widget = window.turnstile.render(node, { sitekey: siteKey, language: node.dataset.language || document.documentElement.lang || "auto" });
node.dataset.widgetId = widget;
});
}
if (window.turnstile) render();
else {
let script = document.querySelector('script[src^="https://challenges.cloudflare.com/turnstile/v0/api.js"]');
if (!script) {
script = document.createElement("script");
script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
script.async = true;
document.head.appendChild(script);
}
script.addEventListener("load", render, { once: true });
}
return () => { disposed = true; if (widget !== undefined) window.turnstile?.remove(widget); };
}
function resetCaptcha(form) {
const widget = form.querySelector("[data-turnstile]")?.dataset.widgetId;
if (widget !== undefined) window.turnstile?.reset(widget);
}<script setup>
import { ref } from "vue";
const status = ref("");
const busy = ref(false);
async function onSubmit(event) {
const form = event.currentTarget;
if (busy.value || !form.reportValidity()) return;
const body = new FormData(form);
body.set("access_key", "fk_your_access_key");
body.set("_lang", "en");
busy.value = true;
status.value = "Sending…";
try {
const response = await fetch("https://formgong.com/submit", {
method: "POST",
headers: { Accept: "application/json" },
body,
});
const result = await response.json();
status.value = result.success ? (result.message || "Sent. Thank you!") : (result.message || "Could not send. Please try again.");
} catch {
status.value = "Connection failed. Please try again.";
} finally {
busy.value = false;
}
}
</script>
<template>
<form style="position:relative;display:grid;gap:16px;max-width:32rem" @submit.prevent="onSubmit" :aria-busy="busy">
<h2>Contact us</h2>
<label>Name * <input type="text" name="name" required autocomplete="name"></label>
<label>Email * <input type="email" name="email" required autocomplete="email"></label>
<label>Message * <textarea name="message" required></textarea></label>
<div aria-hidden="true" style="position:absolute;inset-inline-start:0;top:0;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)"><input name="botcheck" tabindex="-1" autocomplete="off"></div>
<button type="submit" :disabled="busy">Send message</button>
<p role="status" tabindex="-1">{{ status }}</p>
</form>
</template>Create a free form, then open Connect on that form in this browser. If it still has the default name, email and message fields, they are replaced with this form.
| This page | Formcarry contact form generator | |
|---|---|---|
| Fields | Name, email, message. Change them by description or by hand | Name, email, message |
| Code | HTML, Tailwind, Bootstrap 5, React, Vue | HTML, React or Vue, with CSS or Tailwind |
| Where it posts | Formgong, after you add a key | Formcarry, after you add a form ID |
| Telegram alert | Instant on the free plan | Not mentioned on the generator page |
What happens when someone presses Send
The HTML version is a normal form. The browser posts it to /submit on Formgong. Formgong checks the key and the honeypot, stores the message, and only then sends the visitor to a thank-you page. If something fails, such as a wrong key, the visitor sees an error page with the reason instead of a fake “Thanks”. You can send people back to your own thank-you page with a hidden _redirect field. It must point to a domain you added to the form.
The React and Vue versions stay on the page. They send the fields with fetch, read the JSON answer, and write “sent” only when the answer says success: true. A network error or a refused submission is written into the status line, so nobody walks away thinking the message went through when it did not.
Why name, email and message
- Name. One field, not first and last. People write their name the way they want to be addressed.
- Email.
type="email"withautocomplete="email", so phones offer the address and the browser rejects a missing @. - Message. A textarea, required. A contact form with an empty message is noise.
The email Formgong sends you uses the visitor's address as the reply address, so Reply in your mail app answers them. That is why the email field is required here. If you want calls, add a phone field and leave it optional. The HTML form generator sample shows a phone and a subject list already wired.
Spam without a CAPTCHA puzzle
Every copy has a hidden field named botcheck. People never see it. Simple bots fill every input, and Formgong sends the submission to spam when that field has text. Leave the field in the code and keep it hidden.
If a bot gets past it, turn on Cloudflare Turnstile in the form settings. Visitors usually see no puzzle at all. The form checker can read your published page and tell you if the honeypot or the key went missing on the way.
Free limits, email and storage
The contact form code is free to copy. Messages are delivered only after you replace the placeholder key.
Copying the code needs no account. AI generations are limited to 5 an hour and 10 a day for each network, plus 100 a day for the whole site. When a limit is hit, use “Build it yourself, without AI”. That path does not call the model.
On the free plan, Telegram is instant and email is sent for every submission. The free plan is 300 submissions a month. Auto-reply is not on the free plan. Submission data is stored in the EU. Support is Formgong at support@formgong.com.
Your description is sent to the model so it can choose fields. It is not stored. Do not paste passwords, card numbers or other secrets.
Questions
What is a contact form generator?
A tool that writes contact form code for you. This one starts with name, email and message, and gives you HTML, Tailwind, Bootstrap 5, React and Vue versions of the same form.
How do I add a contact form to my website?
Copy the HTML tab, replace fk_your_access_key with your Formgong key, and paste it where the form should appear. Publish the page and send yourself a test message.
Does the form show a success message even if sending fails?
No. The HTML form shows Formgong's thank-you page only after the message is stored. The React and Vue versions say sent only when the server answers success: true.
Will I get the messages by email?
Yes, after you add a real key. On the free plan, Telegram is instant and email is sent for every submission. The free plan is 300 submissions a month.
Can I change the fields?
Yes. Describe the form in the box, or build it yourself without AI. You can add a phone, a subject list, a checkbox or a date.
Sources
Checked on 16.10.2026 against these public pages: MDN: <form>, MDN: input type=email, Formcarry contact form generator, Formgong HTML guide.