Feedback form you can copy
Keep the sample or describe your own. The HTML tab is selected. React and Vue keep the visitor on the page and show the thank-you line only when Formgong answers success: true. Anything else, including a network error, is written into the status line.
Fields, no AI
Live preview
Preview only. Nothing is sent from this page.
Tell us how it went
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>Tell us how it went</h2>
<input type="hidden" name="access_key" value="fk_your_access_key">
<input type="hidden" name="_lang" value="en">
<fieldset><legend>How was your experience? *</legend><label class="fg-choice"><input type="radio" name="experience" value="Great" required> Great</label><label class="fg-choice"><input type="radio" name="experience" value="Good" required> Good</label><label class="fg-choice"><input type="radio" name="experience" value="Okay" required> Okay</label><label class="fg-choice"><input type="radio" name="experience" value="Poor" required> Poor</label></fieldset>
<label>What is it about? * <select name="about" required><option value="">—</option><option value="The website">The website</option><option value="An order">An order</option><option value="Our service">Our service</option><option value="Something else">Something else</option></select></label>
<label>Your feedback * <textarea name="feedback" required></textarea></label>
<label>Name <input type="text" name="name" autocomplete="name"></label>
<label>Email, if you want a reply <input type="email" name="email" autocomplete="email"></label>
<label class="fg-choice"><input type="checkbox" name="contact_ok" value="yes"> You can contact me about this feedback</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 feedback</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">Tell us how it went</h2> <input type="hidden" name="access_key" value="fk_your_access_key"> <input type="hidden" name="_lang" value="en"> <fieldset class="grid gap-2 border-0 p-0"><legend class="font-medium">How was your experience? *</legend><label class="flex items-center gap-2 font-normal" for="tw-experience-0"><input class="h-5 w-5" id="tw-experience-0" type="radio" name="experience" value="Great" required> Great</label><label class="flex items-center gap-2 font-normal" for="tw-experience-1"><input class="h-5 w-5" id="tw-experience-1" type="radio" name="experience" value="Good" required> Good</label><label class="flex items-center gap-2 font-normal" for="tw-experience-2"><input class="h-5 w-5" id="tw-experience-2" type="radio" name="experience" value="Okay" required> Okay</label><label class="flex items-center gap-2 font-normal" for="tw-experience-3"><input class="h-5 w-5" id="tw-experience-3" type="radio" name="experience" value="Poor" required> Poor</label></fieldset> <div class="grid gap-1"><label class="font-medium" for="tw-about">What is it about? *</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-about" name="about" required><option value="">Choose</option><option value="The website">The website</option><option value="An order">An order</option><option value="Our service">Our service</option><option value="Something else">Something else</option></select></div> <div class="grid gap-1"><label class="font-medium" for="tw-feedback">Your feedback *</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-feedback" name="feedback" required></textarea></div> <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"></div> <div class="grid gap-1"><label class="font-medium" for="tw-email">Email, if you want a reply</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"></div> <label class="flex items-center gap-2 font-normal" for="tw-contact_ok"><input class="h-5 w-5" id="tw-contact_ok" type="checkbox" name="contact_ok" value="yes"> You can contact me about this feedback</label> <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 feedback</button> </form>
<form class="position-relative mx-auto" style="max-width:32rem" action="https://formgong.com/submit" method="POST"> <h2 class="h4 mb-3">Tell us how it went</h2> <input type="hidden" name="access_key" value="fk_your_access_key"> <input type="hidden" name="_lang" value="en"> <fieldset class="mb-3"><legend class="form-label mb-1">How was your experience? *</legend><div class="form-check"><input class="form-check-input" type="radio" name="experience" id="bs-experience-0" value="Great" required><label class="form-check-label" for="bs-experience-0">Great</label></div><div class="form-check"><input class="form-check-input" type="radio" name="experience" id="bs-experience-1" value="Good" required><label class="form-check-label" for="bs-experience-1">Good</label></div><div class="form-check"><input class="form-check-input" type="radio" name="experience" id="bs-experience-2" value="Okay" required><label class="form-check-label" for="bs-experience-2">Okay</label></div><div class="form-check"><input class="form-check-input" type="radio" name="experience" id="bs-experience-3" value="Poor" required><label class="form-check-label" for="bs-experience-3">Poor</label></div></fieldset> <div class="mb-3"><label class="form-label" for="bs-about">What is it about? *</label><select class="form-select" id="bs-about" name="about" required><option value="">Choose</option><option value="The website">The website</option><option value="An order">An order</option><option value="Our service">Our service</option><option value="Something else">Something else</option></select></div> <div class="mb-3"><label class="form-label" for="bs-feedback">Your feedback *</label><textarea class="form-control" id="bs-feedback" name="feedback" rows="4" required></textarea></div> <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"></div> <div class="mb-3"><label class="form-label" for="bs-email">Email, if you want a reply</label><input class="form-control" id="bs-email" type="email" name="email" autocomplete="email"></div> <div class="mb-3 form-check"><input class="form-check-input" type="checkbox" id="bs-contact_ok" name="contact_ok" value="yes"><label class="form-check-label" for="bs-contact_ok">You can contact me about this feedback</label></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 feedback</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>{"Tell us how it went"}</h2>
<fieldset><legend>{"How was your experience?"} *</legend><label className="fg-choice"><input type="radio" name="experience" value={"Great"} required /> {"Great"}</label><label className="fg-choice"><input type="radio" name="experience" value={"Good"} required /> {"Good"}</label><label className="fg-choice"><input type="radio" name="experience" value={"Okay"} required /> {"Okay"}</label><label className="fg-choice"><input type="radio" name="experience" value={"Poor"} required /> {"Poor"}</label></fieldset>
<label>{"What is it about?"} * <select name="about" required><option value="">—</option><option value={"The website"}>{"The website"}</option><option value={"An order"}>{"An order"}</option><option value={"Our service"}>{"Our service"}</option><option value={"Something else"}>{"Something else"}</option></select></label>
<label>{"Your feedback"} * <textarea name="feedback" required /></label>
<label>{"Name"} <input type="text" name="name" autoComplete="name" /></label>
<label>{"Email, if you want a reply"} <input type="email" name="email" autoComplete="email" /></label>
<label className="fg-choice"><input type="checkbox" name="contact_ok" value={"yes"} /> {"You can contact me about this feedback"}</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 feedback"}</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>Tell us how it went</h2>
<fieldset><legend>How was your experience? *</legend><label class="fg-choice"><input type="radio" name="experience" value="Great" required> Great</label><label class="fg-choice"><input type="radio" name="experience" value="Good" required> Good</label><label class="fg-choice"><input type="radio" name="experience" value="Okay" required> Okay</label><label class="fg-choice"><input type="radio" name="experience" value="Poor" required> Poor</label></fieldset>
<label>What is it about? * <select name="about" required><option value="">—</option><option value="The website">The website</option><option value="An order">An order</option><option value="Our service">Our service</option><option value="Something else">Something else</option></select></label>
<label>Your feedback * <textarea name="feedback" required></textarea></label>
<label>Name <input type="text" name="name" autocomplete="name"></label>
<label>Email, if you want a reply <input type="email" name="email" autocomplete="email"></label>
<label class="fg-choice"><input type="checkbox" name="contact_ok" value="yes"> You can contact me about this feedback</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 feedback</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.
| Field | Type | Required |
|---|---|---|
| How was your experience? | Radio: Great, Good, Okay, Poor | Yes |
| What is it about? | Select with four topics | Yes |
| Your feedback | Textarea | Yes |
| Name, email, permission to reply | Text, email, checkbox | No |
Why the sample asks what it asks
- The rating first. Four words, from Great to Poor, as radio buttons. A rating is the easiest thing to answer, and it tells you how to read the comment.
- The topic. A short list, so you can sort feedback about the website from feedback about an order without reading every message.
- The comment. The only required text. Feedback without words is a score, and a score alone rarely tells you what to fix.
- Name and email, optional. People are more candid when they can stay anonymous. Those who want an answer will fill in the email.
- Permission to reply. A separate checkbox, not ticked. It records that the person agreed to be contacted.
Answering feedback
When the person leaves an email, the notification Formgong sends you uses that address as the reply address. Press Reply in your mail app and you are writing to them. When the email is empty, there is nobody to answer, so read it, fix what you can and move on.
For a team, send feedback to a shared channel: Slack, Discord or Telegram. Everyone sees the poor ratings on the day they arrive.
Private feedback and public reviews
This form is private. What people write goes to you, not to your website. If you want customers to leave a public review, that is a different form. Formgong’s review widget collects reviews and shows them on your site, and you choose whether new reviews wait for your approval. The Google review link generator gives you the link that opens Google’s review box for your business.
Many shops use both: a private form for “what went wrong”, and a review link for happy customers. The Ocra template has a working five-face feedback form if you want to see one inside a whole site.
Free limits, email and storage
The feedback form code is free to copy. Feedback is 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 1,000 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 feedback form generator?
A tool that writes a feedback form for your website. This one starts with a rating, a topic, a comment and optional contact details, and gives you HTML, Tailwind, Bootstrap 5, React and Vue.
How do I add a feedback form to my website?
Copy the HTML tab, replace fk_your_access_key with your Formgong key and paste the form into your page. Publish it and send yourself one test.
Can people leave feedback anonymously?
Yes. In the sample, name and email are optional. Delete them if you never want contact details.
Is the feedback published on my site?
No. It goes to your inbox, email or Telegram. Public reviews are a separate product, the Formgong review widget.
What does the visitor see after sending?
The HTML form shows a thank-you page only after the feedback is stored. The React and Vue versions show thanks only when the answer says success: true.
Sources
Checked on 16.10.2026 against these public pages: MDN: <textarea>, MDN: input type=radio, W3C WAI: Grouping controls.