Skip to content
New: Customize your theme with an AI assistant. Read the guide

Forms

Files: src/config/config.toml, form fields in content files · Skill level: Beginner

Homio is a static site: it has no server of its own to receive messages. Each form sends its answers to a form service you choose, which emails them to you or keeps them in a dashboard. You set that service once in config.toml.

The Forms And Where They Send

FormWhere it appearsFields are inSends to
Contact/contact/, the quote contact on the main homepagesrc/content/sections/english/contact-section.md (or a page’s contactSection block)contactFormAction
Walkthrough request/industries/, industry pages, commercial homepage and service pagesthe page’s contactSection blockcontactFormAction
Gift card and referral requests/gift-cards/, /referral/contactSection in pages/english/gift-cards.md and referral.mdcontactFormAction
Job applicationjob pages, optionally /careers/#applyapplication.form in careers/english/-index.mdcontactFormAction
Hero quote formheroes with the form layout (/home-cleaning/)form in the heroSection blockcontactFormAction
Booking request/book/, and the booking card on /home-cleaning/src/content/sections/english/booking-section.md[settings.booking] formAction, else contactFormAction
Newsletter signupblog pages, default and large footers, CTA newsletter layoutsubscription in src/i18n/en.json[settings.subscription] formAction (Mailchimp)

Every form except the newsletter uses the same provider, contactFormProvider. The booking form has its own guide: Booking Request.

Connect The Forms

The theme ships with placeholder addresses. Until you replace them, messages go nowhere: npm run dev shows a yellow warning inside each form, and both npm run dev and npm run build print a [forms] warning in the terminal. Visitors of a deployed site never see the notice, so check the terminal before you deploy.

Pick a provider and set both lines in [settings] of config.toml:

Free, no account. Messages arrive by email as a table.

[settings]
contactFormAction = "https://formsubmit.co/[email protected]"
contactFormProvider = "formsubmit.co"

The first message triggers an activation email from Formsubmit. Open it and confirm, or later messages won’t be delivered.

Booking requests to another inbox. Leave formAction = "" in [settings.booking] to send bookings with the contact form, or set your own address (same provider) to keep them apart:

[settings.booking]
formAction = "https://formsubmit.co/[email protected]"

After launch, send one real message from each form on the live site to make sure it arrives.

The Newsletter (Mailchimp)

  1. In Mailchimp, open Audience → Signup forms → Embedded forms, and copy the URL in <form action="..."> from the embed code.
  2. Paste it as formAction in [settings.subscription] of config.toml, in place of the placeholder with YOUR_USER_ID and YOUR_LIST_ID.
  3. The embed code writes &amp; between parameters: change each one to a plain &. The dev server warns if one is left.
[settings.subscription]
enable = true # false hides every signup form and the newsletter section
formAction = "https://your-account.us1.list-manage.com/subscribe/post?u=YOUR_USER_ID&id=YOUR_LIST_ID&f_id=YOUR_FORM_ID"
mailchimpTagValue = "" # optional Mailchimp tag id added to every signup

Mailchimp’s bot field is added for you from the u and id in the URL. The form’s messages (sending, success, already subscribed, errors) and its title and consent line are in subscription of src/i18n/en.json and fr.json. title and note in [settings.subscription] replace them on a single-language site.

Change The Fields

Every form except the booking form and the newsletter is written the same way: a form with an emailSubject, a submitButton, an optional note, and a list of inputs. For the contact page, edit form in src/content/sections/english/contact-section.md (and french/):

form:
emailSubject: "New message from the website" # subject of the email the provider sends you
submitButton:
enable: true
label: "Send message"
# Shown under the button. {phone} {email} {name} come from [business]
note: "We use your details only to reply. [Privacy policy](/privacy-policy/)"
inputs:
- label: "Name"
name: "Name" # the field name in the message you receive
placeholder: "Your name"
required: true
halfWidth: true # two fields per row on wider screens
- label: "Message"
name: "Message"
tag: "textarea"
rows: "5"
required: true
Field typeHow to write it
TextNo type, or type: "text"
Email, phone, number, date, web addresstype: "email", "tel", "number", "date", or "url"
Long texttag: "textarea" and rows
Radio buttonstype: "radio", one item per choice, with the same name and group; the first carries groupLabel (the question)
Checkboxestype: "checkbox", one item per choice, with a shared group
Dropdowndropdown: with type (select or search) and items (label, value, optional selected and hint). source: "areas" adds one option per service area

Other options: placeholder, required, defaultValue, value, checked, and id. The full list is inputFieldSchema in src/sections.schema.ts. A dropdown item’s hint shows a line of Markdown under the field while it is chosen, as the “An existing booking” topic does.

The job application can put {role} and {area} in its emailSubject, and sends the role in a hidden field. The gift card form adds the chosen amount to its subject.

Success And Error Messages

The last two items of inputs are not fields. The one with note: success holds the message shown after a message is sent; the one with note: deprecated the message shown when sending fails. Edit their content and keep their parentClass:

- note: success
parentClass: "hidden text-sm message success"
content: "Thanks. We reply the same working day, usually by phone." # sample promise
- note: deprecated
parentClass: "hidden text-sm message error"
content: "That didn't send. Call {phone} or email {email}." # {phone} {email} from [business]

{phone} and {email} are filled in from [business] in config.toml. Make the success message a promise you keep. Field error messages (“Check this field.”) are in form of src/i18n/en.json; the booking form’s messages are in booking-section.md (success) and booking of the i18n files.

Without JavaScript, every form still sends: it posts straight to the provider, which shows its own confirmation page. Booking requests sent this way land on /book/thanks/ (thankYouUrl in [settings.booking]).

Spam Protection

Every form except the newsletter has two built-in traps, with no captcha to solve (the newsletter uses Mailchimp’s own hidden bot field):

  • A hidden field that people never see but bots fill in. Its name matches your provider: _honey (Formsubmit), _gotcha (Formspree), or bot-field (Netlify).
  • A time trap: a form sent less than 4 seconds after the visitor first touched it is dropped without a request.

Your provider may filter spam on top of this: check its settings and documentation.

Privacy

Each form’s note links to your privacy policy, and the booking form has a consent checkbox (consent in booking-section.md). Keep them, point the links at your real pages, and list your form provider in src/content/pages/english/privacy-policy.md.

The section files have no schema, so npm run astro-check doesn’t report a mistyped option there. Open each form, fill it in, and send a test after each change.

Do It With AI

Connect my forms to [Formsubmit with EMAIL | Formspree with form ID | Netlify Forms]: set
contactFormAction and contactFormProvider in src/config/config.toml following its comments, and
[leave [settings.booking] formAction empty | set it to BOOKING ENDPOINT]. Set the Mailchimp URL
[URL] as formAction in [settings.subscription], with every &amp; changed to &. Then run npm run dev
and tell me if any [forms] warning is left.
Change the contact form in src/content/sections/english/contact-section.md (and french/) to ask
for: [FIELDS]. Keep the form structure, the note with the privacy link, and the two message items
at the end of inputs (note: success and note: deprecated). Use {phone} and {email} in the error
message.
Type something to search...