Contact form
A contact form earns its place by routing, not by asking. Four fields is the whole job — the value is in where the message goes next and how fast you see it.
The form beside this text is live. Fill it in and submit it — it validates exactly as it would for a real respondent, and saves nothing.
A subject field that sorts itself
Making the subject a dropdown rather than free text means enquiries arrive pre-categorised, so sales and support can be filtered apart without anyone reading them first.
Land it in Slack as it arrives
Point the form at a Slack channel and each enquiry appears where your team already is. Destinations are additive, so the dashboard copy and the CSV export stay available regardless.
No card required.
Contact us
Live previewHow to build this form
5 steps in the editor. Nothing here needs a paid plan.
- Add short text and email fields. Two fields is genuinely enough to reply to someone.
- Add a dropdown for the subject rather than a text box, so enquiries arrive already sorted into sales, support and everything else.
- Add a required long text field for the message itself.
- In Settings, add a Slack destination so each enquiry appears where your team already is, or a webhook to reach your own system.
- Embed the link on your contact page. Nobody needs an account to send you a message.
Fields in this form
4 fields, using 4 of the 19 field types available.
| Question | Field type | Required |
|---|---|---|
| Name | Short text | Yes |
| Yes | ||
| What is this about? | Select | Yes |
| Message | Long text | Yes |
A contact form for a static site, with no backend
A plain HTML <form> is only half a contact form. The markup is trivial — a few inputs and a submit button — but the moment someone presses it, the browser needs somewhere to POST to, and that somewhere has to accept the request, validate it, store it, and tell you it happened. On a static site there is nothing to POST to. That is the actual problem, and no amount of HTML and CSS solves it.
This is where a hosted form fits. You keep your static site exactly as it is — no server, no PHP mail() call, no serverless function to maintain, no SMTP credentials sitting in a config file — and the form runs in an iframe from somewhere that already does the receiving. Two lines of HTML: a div carrying the form ID, and a script tag. The script injects the frame and resizes it as the form grows, so there is no fixed height to guess at and no inner scrollbar.
If your host strips script tags — Notion does, and so do the cheaper tiers of several site builders — a plain iframe works instead, at a height you set. Either way your page ships as static files and nothing on your side handles a POST.
Where a message actually goes
Every submission is stored in Seagit first, and that copy is the record: it appears in your dashboard and exports to CSV whenever you want it. Everything else is additive. You can post each enquiry into a Slack or Discord channel, or POST it to your own endpoint as JSON with an optional auth header.
One thing this does not do, and it is worth knowing before you build around it: there is no email notification. Turning on email collection adds a field that records the sender's address so you can reply — it does not send you a message when something arrives. If you want to be told, use the Slack destination, or point a webhook at something that emails you. Assuming an email that never comes is a bad way to discover this.
Webhook delivery is best-effort by design: each destination gets five seconds and one attempt, with no retries. If your endpoint is down when an enquiry lands, that delivery is simply not made. The enquiry is still in Seagit and still in the export, so reconcile against those rather than against what your own system received.
Spam, honestly
Automated submissions are checked with Cloudflare Turnstile before anything is stored, on every plan, with nothing to switch on. That is one layer, not the whole thing: there is still no honeypot and no rate limit, so a determined human can still type junk into a public form. It also fails open on purpose, on OUR side — if our server cannot reach Cloudflare, or the check is misconfigured at our end, the submission is accepted rather than refused, because a verification outage of ours must not become a collection outage for you, and some spam gets through for the duration. That does not extend to the respondent's browser: if theirs cannot load Cloudflare at all — an ad blocker, a corporate filter — nothing arrives to verify, which is indistinguishable from a bot, and the submission is refused. Their answers stay on screen, and pressing Submit again is the thing to try — the script is often merely slow — though a blocker left on will keep refusing. That last case is what the paid third option answers: set a form to keep suspected spam for review and nobody is refused at all — a failed submission is stored out of your main list, goes to none of your destinations, and waits behind a “Suspected spam” toggle until you unflag it (optionally sending it on) or delete it. Quarantined rows share your response limit rather than adding to it, and they are the first thing dropped to make room: once the form is full, storing anything new deletes the oldest suspected-spam row, silently, and a real response is never dropped for one. So spam can use up spare room but can never cost you a response you paid for. The rest of the layered protection is designed and not built.
What you can do today is narrow the exposure. Restricting which sites may embed the form means the browser refuses to frame it anywhere you have not listed, so a scraped snippet will not render on someone else's page. A password turns the form private entirely, which suits an internal contact form but defeats a public one. Beyond that, the honest answer is that you will be deleting some rows.
The practical mitigation is where the enquiries land. Because everything is stored centrally and exported as CSV, sorting real messages from junk is filtering a spreadsheet rather than cleaning out an inbox — and a webhook that forwards into your own system can apply whatever filtering you already run there.
What goes wrong with this form
Specific to a contact form, not general advice about forms.
Relying on one destination
Slack and webhooks are additive, not a replacement: the response is stored in Seagit regardless. That matters because a webhook gets five seconds and one attempt with no retries, so treating either as your system of record will eventually lose you an enquiry.
No subject field
A dropdown of enquiry types costs the sender one click and routes the message before anyone reads it. Without it every enquiry arrives looking equally urgent, which in practice means none of them do.
Embedding it and never testing the embed
If you add an allowed-sites list and forget to include the exact origin — scheme and port included — the browser refuses the frame and your contact form renders as an empty box, with no error anywhere. Load the page you embedded it on before you consider it done.
Questions about this form
How do I add a contact form to my website?
Build the form, then paste two lines of HTML into your page: a div carrying the form ID and a script tag loading embed.js. The form renders inside an iframe, so your styles cannot break it and it cannot break your page. A plain iframe works where scripts are blocked.
Where do contact form submissions go?
Every enquiry is stored in your dashboard and exports to CSV. On top of that you can post each one into a Slack channel or POST it to your own endpoint as JSON. Those destinations are additive — nothing replaces the copy held in Seagit.
Can I add a contact form to a static site with no backend?
Yes, and that is the usual reason to use one. A plain HTML form needs somewhere to POST to, which a static site does not have. Paste two lines — a div with the form ID and a script tag — and the form runs in an iframe from a host that already does the receiving. No server, no mail() call, no SMTP credentials in a config file.
Will I get an email when someone submits the contact form?
No. There is no email notification. Collecting the sender’s email adds a field recording their address so you can reply; it does not message you when something arrives. To be told, post enquiries into a Slack channel, or point a webhook at something that emails you. Every enquiry is stored in your dashboard and CSV export regardless.
How do you stop contact form spam?
Every public form is checked for automated submissions with Cloudflare Turnstile — on by default, on every plan, and invisible to almost every respondent. That covers bots, which is most form spam. If the check cannot run on our side the submission is accepted rather than refused, so a verification problem of ours never stops your form collecting — though a respondent whose own browser blocks Cloudflare will be refused, because nothing arrives to verify. On a paid plan you can set a form to keep a failed submission for review instead of refusing it: it is held behind a “Suspected spam” toggle on the responses screen and is sent to none of your destinations until you unflag it — which is how a real person Cloudflare misjudged stops being lost. It does not stop a person typing junk by hand, and there is still no rate limit or honeypot; reviewing flagged submissions does not change that, since hand-typed junk passes the check and lands in your ordinary responses. For that, restrict which sites may embed the form, close it on a date, cap its responses, or password-protect it. Because everything lands in one place and exports to CSV, filtering what does get through is a spreadsheet task rather than an inbox one.
Do I need to write any code for a contact form?
No. The fields are dragged in, and embedding is a copy-paste snippet — no HTML form markup, no server to receive the POST, and no email plumbing to configure. Writing code is optional and only needed if you want submissions in your own system.