Contact form
The Contact section’s form has no JavaScript by design — it submits to your chosen form provider, who handles the email, spam filtering, and (with most providers) sends you a notification. You set the provider once in site.ts.
The shape
Section titled “The shape”contactForm: { provider: "formspree" as "formspree" | "formsubmit" | "netlify" | null, formspreeId: "your-form-id", // formsubmitEmail: "you@example.com", // Netlify: just set provider to "netlify" — no extra fields.}If provider is null (or missing required fields), the form falls back to a mailto: action so it still does something useful.
Provider setup
Section titled “Provider setup”Formspree (recommended)
Section titled “Formspree (recommended)”Free tier: 50 submissions/month. No domain restriction.
- Sign up at formspree.io.
- Create a new form. Copy the form ID — it’s the last segment of the action URL:
https://formspree.io/f/xyzabc123→xyzabc123. - Set:
contactForm: { provider: "formspree", formspreeId: "xyzabc123",}The rendered form:
<form name="contact" method="POST" action="https://formspree.io/f/xyzabc123"> <!-- your fields --></form>Submissions land in your Formspree dashboard and are emailed to the address on the form’s settings page.
FormSubmit
Section titled “FormSubmit”Free, no signup. Just your email address.
- Set:
contactForm: { provider: "formsubmit", formsubmitEmail: "you@yourdomain.com",}- Submit the form once with any data — FormSubmit will email you a confirmation link to verify ownership. Click it. After that, real submissions deliver to your inbox.
The rendered form:
<form name="contact" method="POST" action="https://formsubmit.co/you@yourdomain.com">Netlify Forms
Section titled “Netlify Forms”Only works when you deploy to Netlify. Free up to 100 submissions/month/site.
- Set:
contactForm: { provider: "netlify" }- Deploy to Netlify. The first deploy registers the form.
- Submissions appear in your Netlify dashboard → Forms tab.
The rendered form (Netlify-specific):
<form name="contact" method="POST" data-netlify="true"> <input type="hidden" name="form-name" value="contact" /> <!-- your fields --></form>The data-netlify="true" attribute and the hidden form-name input are what Netlify’s build pipeline scans for.
No provider (null)
Section titled “No provider (null)”contactForm: { provider: null }Falls back to a mailto: action — clicking submit opens the user’s email client with the message body prefilled. Not great UX, but the form isn’t broken.
How it works
Section titled “How it works”Open src/components/Contact.astro. The frontmatter resolves the provider into the right action / method / hidden inputs at build time. The form HTML it emits is plain — no client JS, works without JavaScript enabled.
if (cf.provider === "formspree" && cf.formspreeId) { action = `https://formspree.io/f/${cf.formspreeId}`;} else if (cf.provider === "formsubmit" && cf.formsubmitEmail) { action = `https://formsubmit.co/${cf.formsubmitEmail}`;} else if (cf.provider === "netlify") { netlify = true;} else { action = `mailto:${contactEmail}`; method = "GET";}Adding a custom provider
Section titled “Adding a custom provider”If you use Web3Forms, Basin, getform.io, or something else — edit Contact.astro and add a new branch:
} else if (cf.provider === "web3forms" && cf.web3formsAccessKey) { action = "https://api.web3forms.com/submit"; hiddenAccessKey = cf.web3formsAccessKey;}…then add the new option to the provider union in site.ts.
Common gotchas
Section titled “Common gotchas”What’s next
Section titled “What’s next”- Newsletter — same pattern, different providers.
- Customize → Section data — change the Contact section’s headings, labels, and reply-to email.