Folder: src/blocks/contact/
Components
| Component | File | Purpose |
|---|---|---|
| Contact01 | Contact01.astro |
Section wrapper with heading and email |
| ContactForm | ContactForm.astro |
Form markup — used inside Contact |
Config
Block copy (blocks.ts):
contact: {
width: "narrow",
heading: "Get in touch",
subheading: "Tell me about your project.",
email: "[email protected]",
responseNote: "I typically reply within one business day.",
},
Form provider (integrations.ts → contactForm):
contactForm: {
provider: "formspree", // formsubmit | netlify | cloudflare | mailto | null
formspreeId: "your-id",
showSubject: true,
redirectPath: "/contact/",
},
Form providers
Pick one backend in src/config/integrations.ts under contactForm.provider. ContactForm.astro calls resolveContactForm() in src/lib/contact-form.ts, which sets the form action, method, hidden fields, and provider-specific attributes (Netlify data-netlify, Cloudflare Turnstile, etc.).
Quick comparison
| Provider | Best for | Setup effort |
|---|---|---|
null / mailto |
Local demo, no backend | None (unreliable in production) |
formspree |
Quick launch, low traffic | Low |
formsubmit |
Free tier, no account | Low |
netlify |
Sites hosted on Netlify | Low (if on Netlify) |
cloudflare |
Same-domain POST, Turnstile, no third-party form SaaS | Medium |
Formspree
- Create a form at formspree.io
- Set
provider: "formspree"andformspreeIdto your form ID
contactForm: {
provider: "formspree",
formspreeId: "xyzabcde",
showSubject: true,
redirectPath: "/contact/",
},
Submissions are delivered to the email you configure in Formspree. The form POSTs directly to https://formspree.io/f/{formspreeId}.
FormSubmit
- Set
provider: "formsubmit"andformsubmitEmailto your inbox - Submit the form once and confirm the address via the email FormSubmit sends
contactForm: {
provider: "formsubmit",
formsubmitEmail: "[email protected]",
formsubmitSubject: "New contact form submission",
successRedirect: "/contact/?sent=1#contact-form",
showSubject: true,
},
successRedirect adds a hidden _next field so visitors return to your site with ?sent=1#contact-form after a successful submission. The inline script in ContactForm.astro shows the success message when that query param is present.
Netlify Forms
- Host the site on Netlify
- Set
provider: "netlify" - Deploy — Netlify detects
data-netlifyon the form at build time
contactForm: {
provider: "netlify",
formName: "contact",
redirectPath: "/contact/",
},
Submissions appear in the Netlify dashboard under Forms. A honeypot field (bot-field) is included automatically.
Cloudflare Worker
Recommended for production on Cloudflare when you want same-domain POST handling, server-side Turnstile verification, and no Formspree/FormSubmit dependency.
This pattern keeps the Astro site fully static while a Worker handles POST /contact/ on your domain. Included in theme packages:
- Server-side Turnstile verification
- Honeypot field (
website) - Email delivery via Cloudflare Email Routing /
send_emailbinding - Redirect back with
?sent=1#contact-form
Tradeoffs: more setup than Formspree (Wrangler, secrets, verified email destination); Cloudflare-specific — not portable to Vercel/Netlify without a different backend.
- Set in
integrations.ts:
contactForm: {
provider: "cloudflare",
action: "/contact/",
redirectPath: "/contact/",
turnstileSiteKeyPlaceholder: "__TURNSTILE_SITE_KEY__",
},
-
Copy
wrangler.jsonc.example→wrangler.jsoncand update domain +destination_address -
Set Worker secrets:
npx wrangler secret put TURNSTILE_SECRET_KEY
npx wrangler secret put TURNSTILE_SITE_KEY
- Build and deploy:
npm run build
npx wrangler deploy
The Worker replaces __TURNSTILE_SITE_KEY__ in HTML at runtime so the same static build works across environments. See src/worker.ts and Deploy for hosting details.
mailto fallback
When provider is null, the form falls back to mailto: — fine for demos, not recommended for production (behavior varies by browser and OS).
contactForm: {
provider: null,
},
Related files
| File | Purpose |
|---|---|
src/config/integrations.ts |
Provider selection and credentials |
src/lib/contact-form.ts |
Resolves action, method, hidden fields |
src/blocks/contact/ContactForm.astro |
Shared form markup |
src/blocks/contact/Contact01.astro |
Contact block wrapper |
src/worker.ts |
Optional Cloudflare handler (theme packages) |
wrangler.jsonc.example |
Example Worker config |
Add to a page
---
import Contact01 from "@/blocks/contact/Contact01.astro";
---
<Contact01 />
Inline:
<Contact
heading="Start a project"
email="[email protected]"
responseNote="Replies within 24 hours."
/>
ContactForm is rarely used standalone but can be:
---
import ContactForm from "@/blocks/contact/ContactForm.astro";
---
<ContactForm email="[email protected]" showSubject={false} />
Props
Contact
| Prop | Config key |
|---|---|
width |
contact.width |
heading / subheading |
contact.* |
email |
contact.email |
responseNote |
contact.responseNote |
ContactForm
| Prop | Config key |
|---|---|
email |
contact.email |
redirectPath |
contactForm.redirectPath |
showSubject |
contactForm.showSubject |
Dedicated contact page
src/pages/contact.astro typically wraps the section:
---
import BaseLayout from "@/layouts/BaseLayout.astro";
import Contact01 from "@/blocks/contact/Contact01.astro";
---
<BaseLayout title="Contact" pageType="contact">
<Contact01 />
</BaseLayout>