Copy a Tailwind CSS form
The form below is the example. The Tailwind tab is selected. HTML, Bootstrap 5, React and Vue are the other tabs, from the same fields. Nothing is sent until you replace fk_your_access_key.
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:#fff;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>Topic * <select name="topic" required><option value="">—</option><option value="Product">Product</option><option value="Billing">Billing</option><option value="Something else">Something else</option></select></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-topic">Topic *</label><select 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-topic" name="topic" required><option value="">Choose</option><option value="Product">Product</option><option value="Billing">Billing</option><option value="Something else">Something else</option></select></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-topic">Topic *</label><select class="form-select" id="bs-topic" name="topic" required><option value="">Choose</option><option value="Product">Product</option><option value="Billing">Billing</option><option value="Something else">Something else</option></select></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:#fff;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>{"Topic"} * <select name="topic" required><option value="">—</option><option value={"Product"}>{"Product"}</option><option value={"Billing"}>{"Billing"}</option><option value={"Something else"}>{"Something else"}</option></select></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>Topic * <select name="topic" required><option value="">—</option><option value="Product">Product</option><option value="Billing">Billing</option><option value="Something else">Something else</option></select></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 | Flavio Copes field generator | |
|---|---|---|
| Output | A full form: HTML, Tailwind, Bootstrap 5, React, Vue | One field, with help text and an error state |
| Delivery | Posts to Formgong after you add a key | Markup only. No backend |
| Preview | Formgong styles, not Tailwind | The tool’s own CSS, not Tailwind |
| Account | None to copy the code | None |
What the Tailwind classes do
Tailwind’s preflight removes the browser’s form look. An input with no classes is a thin line. Every control here sets width, padding, a slate border, a white background and a visible focus ring.
- Labels. Each text, email, select and textarea has a
labelwithformatching the controlid. The id istw-plus the field name. - Required. Required fields add
requiredand a star in the label. The browser blocks an empty submit. - Choices. A select uses one name. Radios share a name. A checkbox group uses one name and is not marked required, because plain HTML would require every box.
- Honeypot.
botchecksits in a one-pixel box,aria-hidden,tabindex="-1". Leave it empty. Do not style it back onto the page. - Button. The submit button uses
bg-indigo-700and a 48px minimum height.
Paste this into a page that already loads Tailwind. If the classes do nothing, the stylesheet is missing. This generator does not add a CDN script for you.
The official forms plugin is optional
The @tailwindcss/forms plugin restyles every form on the site. You do not need it for this snippet. The classes above set the border, radius and focus ring themselves.
Use the plugin when the rest of the site should share one form look. If both are on, the plugin and these classes can fight. Check one input in the browser before you ship.
Focus uses focus-visible, so a mouse click does not always draw the ring. Keyboard users still get it. That is the usual Tailwind pattern.
Where a Tailwind form fits
- A marketing site on Tailwind. Drop the snippet into the contact section. Point the action at Formgong. Keep your layout classes around it.
- An AI-built page. Tools such as v0 and Cursor often emit Tailwind. Paste this form instead of asking the model to invent a backend.
- A component later. The React and Vue tabs use the same fields if the page is not plain HTML. They check
result.successand write a failure on the page.
A visual canvas such as Windframe is a better fit when you want to drag fields and see Tailwind paint them live. A one-field lab such as Flavio Copes’s generator is a better fit when you only need help text, an error state and aria-invalid on a single control. This page builds the whole form and the delivery key.
Free limits, email and storage
The Tailwind copy is free. Delivery starts when the placeholder key is a real Formgong 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 Tailwind form generator?
It writes HTML that uses Tailwind utility classes. This page opens on that tab with a contact form: name, email, topic and message. You can describe a different form, or edit the fields without AI.
Does this page load Tailwind CSS?
No. The preview uses Formgong’s own preview styles. The class names work after you paste the code into a site that loads Tailwind.
How do I make a Tailwind CSS contact form?
Copy the Tailwind tab. Put your access key in the hidden access_key field. Publish the page on a host that already includes Tailwind. The HTML guide shows the same kind of form without utility classes.
Do I need the Tailwind forms plugin?
No. These classes set border, padding and focus themselves. Add @tailwindcss/forms only if you want that plugin’s look on every form.
Will the form send email?
After you replace the placeholder key, yes. On the free plan, Telegram is instant and email is sent for every submission. The free plan is 300 submissions a month.
Sources
Checked on 11.10.2026 against these public pages: Tailwind CSS forms plugin, Flavio Copes: Tailwind form generator, tailwindcss-generator.com form tool, Windframe form builder, Formgong HTML guide.