# Forms (Contact, Booking, Newsletter)

## 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
| Form | Where it appears | Fields are in | Sends to |
| --- | --- | --- | --- |
| Contact | `/contact/`, the `quote` contact on the main homepage | `src/content/sections/english/contact-section.md` (or a page's `contactSection` block) | `contactFormAction` |
| Walkthrough request | `/industries/`, industry pages, commercial homepage and service pages | the page's `contactSection` block | `contactFormAction` |
| Gift card and referral requests | `/gift-cards/`, `/referral/` | `contactSection` in `pages/english/gift-cards.md` and `referral.md` | `contactFormAction` |
| Job application | job pages, optionally `/careers/#apply` | `application.form` in `careers/english/-index.md` | `contactFormAction` |
| Hero quote form | heroes with the `form` layout (`/home-cleaning/`) | `form` in the `heroSection` block | `contactFormAction` |
| Booking request | `/book/`, and the booking `card` on `/home-cleaning/` | `src/content/sections/english/booking-section.md` | `[settings.booking] formAction`, else `contactFormAction` |
| Newsletter signup | blog pages, `default` and `large` footers, CTA `newsletter` layout | `subscription` 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](/homio/customization/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`:

<Tabs>
  <TabItem label="Formsubmit (default)">

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

    ```toml
    [settings]
      contactFormAction   = "https://formsubmit.co/you@your-domain.com"
      contactFormProvider = "formsubmit.co"
    ```

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

  </TabItem>
  <TabItem label="Formspree">

    Create a form at [formspree.io](https://formspree.io/) and copy its endpoint. Messages appear in your Formspree dashboard, and Formspree can email them to you.

    ```toml
    [settings]
      contactFormAction   = "https://formspree.io/f/your-form-id"
      contactFormProvider = "formspree"
    ```

  </TabItem>
  <TabItem label="Netlify Forms">

    Only when the site is hosted on Netlify. Netlify detects the forms at deploy time.

    ```toml
    [settings]
      contactFormAction   = "/"
      contactFormProvider = "netlify"
    ```

    Turn on form detection in the Netlify dashboard (**Forms**), deploy, and send a test message. Submissions appear in the dashboard and can be forwarded by email.

  </TabItem>
</Tabs>

**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:

```toml
[settings.booking]
  formAction = "https://formsubmit.co/bookings@your-domain.com"
```

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

<Aside type="caution">
  The forms post to the real address even while you work with `npm run dev`.
  Use your own address before you test, and keep test messages short.
</Aside>

### The Newsletter (Mailchimp)
<Steps>

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.

</Steps>

```toml
[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/`):

```yaml
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 type | How to write it |
| --- | --- |
| Text | No `type`, or `type: "text"` |
| Email, phone, number, date, web address | `type: "email"`, `"tel"`, `"number"`, `"date"`, or `"url"` |
| Long text | `tag: "textarea"` and `rows` |
| Radio buttons | `type: "radio"`, one item per choice, with the same `name` and `group`; the first carries `groupLabel` (the question) |
| Checkboxes | `type: "checkbox"`, one item per choice, with a shared `group` |
| Dropdown | `dropdown:` 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`:

```yaml
    - 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
```text
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.
```

```text
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.
```