Contact Forms
Contact form builder: embed forms on pages, store every submission, send email through the site's provider.
Plugin
Contact Forms
Contact form builder: embed forms on pages, store every submission, send email through the site's provider.
Description
Build a contact form in the admin, drop it on any page with a block, and read every submission in the built-in inbox. Notification email goes through the provider your site already uses — the plugin never talks to an email service itself.
Live demo: emdashhq.com/contact — a working form rendered by this plugin on a production EmDash site.
What you get
- Forms — create and edit forms, manage fields (text, email, tel, url, number, textarea, select, checkbox), reorder them, enable or disable a form without deleting it.
- Submissions inbox — every entry stored in your site’s database with the page it came from; filter by form, status, or email result; mark read or archive; resend the notification; copy as CSV.
- Email log — every send attempt with status, duration, and the provider’s error message, so a delivery failure is diagnosable instead of silent.
- Dashboard widget — unread count, active forms, and the most recent submissions.
- Anti-spam — hidden honeypot, minimum-fill-time trap, and a per-IP hourly limit on by default; optional Cloudflare Turnstile when passive traps are not enough.
- Retention — optionally delete submissions and log entries older than a number of days.
Your visitor’s address never appears in the public form definition, and a form’s recipient is only ever in the admin.
Installation
Install and first form
- In your EmDash admin open Registry, search for Contact Forms, and click Install. It asks to send email through your site’s provider, and to contact
challenges.cloudflare.com— that host is only used if you turn on Turnstile. - Open Plugins → Contact forms → Settings and set the default notification email. This is where submissions are sent when a form does not override it.
- Open Forms → New form. Name it, give it a slug such as
contact, and add fields. Each form can override the recipient, the subject template, the button label, and the success message. - Put the form on a page with the Contact form block and enter the slug. If your theme does not ship the block yet, the package’s
site/directory has the two files to copy —block-type.jsonfor the seed andContactForm.astrofor the renderer; the README walks through it. - In Settings → Email, activate a provider and send a test email. Without a provider, submissions are still stored — they are just not emailed, and the email log shows why.
Submissions appear in Plugins → Contact forms → Submissions and on the dashboard widget as soon as the first visitor writes.
FAQ
Where do submissions go?
Into your site’s own database, under Plugins → Contact forms → Submissions. Nothing is sent to a third party — the only outbound calls are the notification email through your provider and, if enabled, Turnstile verification.
Does it send email without a provider configured?
No, and that is fine — submissions are still stored with the email status skipped. Once you activate a provider under Settings → Email, notifications and visitor confirmations start sending, and the email log records every attempt.
How does it stop spam?
Three passive defenses are always on: a hidden honeypot field, a minimum-fill-time trap, and a per-IP hourly submission limit you can tune in settings. When that is not enough, paste Cloudflare Turnstile site and secret keys into the plugin settings and every form is verified before anything is stored.
Can visitors get a confirmation email?
Yes. A form can send the visitor a confirmation using its own subject template. Subject templates can use values from submitted fields, and replies to notification mail go to the visitor’s email field.
Can I get the submissions out?
The submissions page copies the filtered view as CSV, and an authenticated export route serves the same CSV for scripting or API tokens.
Can submissions be deleted automatically?
Set Keep submissions for (days) in the plugin settings. A scheduled task deletes submissions and email log entries older than the limit; 0 keeps everything.
Does the public form expose the recipient address?
No. The public form route returns only the fields, labels, and options — the recipient and notification settings never leave the admin.
Changelog
1.1.2
- Required checkboxes now reject omitted or invalid values instead of silently passing.
- Submissions are stored before any email work: if the provider fails afterwards, the row is marked failed but the message is never lost, and the visitor still gets a saved response.
- Submit retries are idempotent: a fingerprint over slug, IP and values suppresses duplicate rows and duplicate emails.
- Rejected submissions repopulate the form with per-field errors via a one-time recovery record.
- Turnstile verification has a ten-second deadline and a configurable outage policy (accept by default).
- The per-IP rate limit is atomic and consumes its budget before Turnstile.
cf_field keys are reserved for the submit protocol and rejected by the admin.- CSV export and the admin preview neutralize spreadsheet formula injection.
- Hourly cleanup now also prunes stale rate buckets, recovery records and idempotency claims.
- Docs: live demo link carries UTM parameters.
1.1.1
- Registry listing gains an icon, a banner, screenshots, an install guide, a FAQ, and this changelog. No changes to the plugin itself.
1.1.0
- Optional Cloudflare Turnstile verification on every form: set the site key and secret key in plugin settings and submissions are verified before storing.
- New
network:requestcapability restricted tochallenges.cloudflare.comfor that check. - Verbose debug logging setting, useful while wiring a new email provider.
1.0.0
- First release. Form builder in the admin with eight field types, a submissions inbox, an email delivery log, and a dashboard widget.
- Notification and visitor-confirmation email through the site’s active provider via
ctx.email.send(). - Honeypot, minimum-fill-time, and per-IP hourly rate limit on by default; optional retention cleanup for old submissions.
- Public
formandsubmitroutes plus the Contact form page block shipped insite/.