Skip to content

Forms

The Contact form and the career application form work as soon as you run the site: they validate, show a sending state, say “Thank you”, keep what the visitor typed when something fails, and drop spam with a honeypot field.

Out of the box they run in demo mode: nothing is sent, the fields are logged in the browser console. Fine while you build — pick a destination before you publish.

You want Mode Hosting
Submissions by email, with no server to run Static — a form service (Formspree, Web3Forms, Getform, Basin) Any static host
Submissions in a CRM, a spreadsheet, Slack… Static — a webhook (Zapier, Make, n8n, Pipedream) Any static host
Mailchimp or Resend directly, no third party Server — the site’s own route A host with an Astro adapter

Most sites want static mode: it keeps the site 100 % static and takes two minutes.

  1. Create a form at your service and copy its endpoint URL (Formspree: https://formspree.io/f/<id>).
  2. Copy the example settings and paste the URL:
    Terminal window
    cp .env.example .env
    PUBLIC_FORM_ENDPOINT=https://formspree.io/f/abcdwxyz
    Web3Forms also needs PUBLIC_FORM_ACCESS_KEY.
  3. Restart npm run dev, send a test submission, and check it arrived.
  4. Set the same variable in your host’s dashboard (or keep .env if you build locally) — the value is baked into the pages at build time, so rebuild after changing it.

For Mailchimp or Resend without a third-party service:

  1. Add the adapter for your host: npx astro add vercel (or netlify, node, cloudflare).
  2. Set PUBLIC_FORM_MODE=server and choose where submissions go with FORM_PROVIDER (console, webhook, mailchimp, resend, or several comma-separated), plus that provider’s keys from .env.example.
  3. The forms now post to /api/forms/contact and /api/forms/application on your own site. Every other page stays static.

Every detail — what each service needs, the fields sent, Mailchimp consent settings, writing your own provider — is in Forms reference.

Form Fields
Contact (/contact) First name, last name, email, phone, the services the visitor is interested in, message
Career application (/careers/<role>) First name, last name, email, phone, the role, LinkedIn, other profiles, website, message

The fields are content, not code: they are listed in src/content/pages/contact.json and career.json, each with its name, label, placeholder, type and whether it is required. Add, remove or rename them there — a field arrives at your service under its name, so keep those you already map somewhere. The service checkboxes on the Contact form are a plain list in the same file; update them to match your services.

  • Submit both forms on the live site with a real address, and reply to the test.
  • Check the “Something went wrong” path once: point the endpoint at a wrong URL in a preview build, submit, and confirm the visitor’s text is still there.

The honeypot stops the usual bots, and form services filter spam on their side. Submissions go only where you point them. Add a line under the form (its copy is in contact.json) linking to a privacy page of your own, and a consent checkbox if you plan to add senders to a marketing audience.