Skip to content
BaseLayer Themes

Docs

Contact01

Contact form with details column

Live preview

Let's work together

Fill out the form or email me directly. I take on a limited number of projects each quarter.

Or email hello@example.com· Typically replies within 24 hours

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: "hello@example.com",
  responseNote: "I typically reply within one business day.",
},

Form provider (integrations.tscontactForm):

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

  1. Create a form at formspree.io
  2. Set provider: "formspree" and formspreeId to 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

  1. Set provider: "formsubmit" and formsubmitEmail to your inbox
  2. Submit the form once and confirm the address via the email FormSubmit sends
contactForm: {
  provider: "formsubmit",
  formsubmitEmail: "you@example.com",
  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

  1. Host the site on Netlify
  2. Set provider: "netlify"
  3. Deploy — Netlify detects data-netlify on 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_email binding
  • 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.

  1. Set in integrations.ts:
contactForm: {
  provider: "cloudflare",
  action: "/contact/",
  redirectPath: "/contact/",
  turnstileSiteKeyPlaceholder: "__TURNSTILE_SITE_KEY__",
},
  1. Copy wrangler.jsonc.examplewrangler.jsonc and update domain + destination_address

  2. Set Worker secrets:

npx wrangler secret put TURNSTILE_SECRET_KEY
npx wrangler secret put TURNSTILE_SITE_KEY
  1. 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,
},
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="you@domain.com"
  responseNote="Replies within 24 hours."
/>

ContactForm is rarely used standalone but can be:

---
import ContactForm from "@/blocks/contact/ContactForm.astro";
---

<ContactForm email="hello@example.com" 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>

← Contact versions · Blocks overview