Skip to content

Contact forms · 4 fields

Contact us form template

Built for a company or team site where more than one person answers enquiries. The topic field is what separates it from a personal contact form: you know who should reply before you open the message.

Get the code

Contact us

Live demo: try the fields. Submitting sends nothing.

Why these fields

  • Name

    text · required

    So the reply can be addressed to a person. One field is enough; splitting first and last name adds friction and does not help you answer.

  • Email

    email · required

    The reply address and the only contact detail you need. A field named email becomes the Reply-To header on the notification email, so you can answer straight from it with no extra setup.

  • Topic

    select · optional

    Optional on purpose. People who cannot decide pick Other, which is better than abandoning the form. Your team can sort and filter on this value in the dashboard.

  • Message

    textarea · required

    Required, because an empty message gives you nothing to act on. A plain textarea with no character minimum keeps short questions welcome.

When to use it

Use it on the contact page of a business site where sales, support and billing questions all arrive in one place. If one person answers everything, the simple contact template is shorter and fits better.

Keep the topic options to the teams that actually reply. Four choices that map to real people beat ten that all end up with the same person.

Privacy and GDPR

  • Answering an enquiry is what the person asked for, so a contact form rarely needs a consent checkbox. Link your privacy notice next to the submit button instead.
  • If you also want to send newsletters, add a separate checkbox that is unticked by default. Never make it a condition of sending the message.
  • Do not add a phone field unless someone will actually call back. Every extra field is personal data you have to store and protect.

Choose your stack. Copy your form.

Replace the example endpoint with your Formward endpoint, then adapt the fields to your needs.

<form action="https://forms.formward.eu/f/your-form-id" method="POST">
  <label for="name">Name</label>
  <input type="text" name="name" id="name" required />
  <label for="email">Email</label>
  <input type="email" name="email" id="email" required />
  <label for="topic">Topic</label>
  <select name="topic" id="topic">
    <option value="Sales">Sales</option>
    <option value="Support">Support</option>
    <option value="Billing">Billing</option>
    <option value="Other">Other</option>
  </select>
  <label for="message">Message</label>
  <textarea name="message" id="message" required></textarea>
  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="position:absolute;left:-9999px" aria-hidden="true" />
  <button type="submit">Send</button>
</form>

Accessible by default, and how to keep it that way

  • Every field in the code has a visible label tied to it with for and id, so screen readers announce what to enter and a click on the label focuses the field.
  • Required fields carry the required attribute, so the browser explains a missing answer before anything is sent. Say “required” in words as well; an asterisk or a colour alone is easy to miss.
  • The spam trap field (_gotcha) is hidden from screen readers with aria-hidden and skipped by the Tab key, so people never meet it.
  • In the JavaScript versions, show the confirmation or error in an element with role="status" so the result is announced, and keep the entered answers when sending fails.

Changed the fields? Paste your version into the HTML form tester to see exactly what it sends, or run it through the GDPR form checker.

How the code talks to Formward

Each form you create gets its own endpoint in the form https://forms.formward.eu/f/your-form-id. The HTML version posts to it directly and the browser is redirected to a thank-you page, so it works without JavaScript.

The Next.js version below sends the same fields with fetch and an Accept: application/json header. Formward then answers with { ok: true, id }, or an error body with 402 when the plan limit is reached and 429 when the rate limit kicks in. See responses and status codes.

Next.js client component

"use client";

import { useState } from "react";

export function ContactUsForm() {
  const [sent, setSent] = useState(false);

  async function onSubmit(e: React.FormEvent<HTMLFormElement>) {
    e.preventDefault();
    const form = e.currentTarget;
    const res = await fetch("https://forms.formward.eu/f/your-form-id", {
      method: "POST",
      headers: { Accept: "application/json" },
      body: new FormData(form),
    });
    if (res.ok) {
      setSent(true);
      form.reset();
    }
  }

  if (sent) return <p>Thanks! Your message has been sent.</p>;

  return (
    <form onSubmit={onSubmit}>
      <label htmlFor="name">Name</label>
      <input type="text" name="name" id="name" required />
      <label htmlFor="email">Email</label>
      <input type="email" name="email" id="email" required />
      <label htmlFor="topic">Topic</label>
      <select name="topic" id="topic">
        <option value="Sales">Sales</option>
        <option value="Support">Support</option>
        <option value="Billing">Billing</option>
        <option value="Other">Other</option>
      </select>
      <label htmlFor="message">Message</label>
      <textarea name="message" id="message" required />
      <input
        type="text"
        name="_gotcha"
        tabIndex={-1}
        autoComplete="off"
        style={{ position: "absolute", left: "-9999px" }}
        aria-hidden="true"
      />
      <button type="submit">Send</button>
    </form>
  );
}

Spam protection

  • The generated code already includes the hidden _gotcha honeypot. Leave it in: a bot that fills it gets a normal success response, the submission is filed as spam and no email is sent.
  • Every form has a per-IP rate limit, and you can restrict it to your own domains with the allowed-origins list in the form settings.
  • Cloudflare Turnstile is optional and works on every plan, including Free. Turn it on in the form settings, add the widget to your markup and Formward checks the token server-side.
  • The form settings can also reject disposable email addresses, which is worth turning on when a fake address would waste your time.

What happens after submit

  • Every plan, including Free, emails each submission to your verified notification address and stores it in the EU, where you can search, tag and reply from the dashboard.
  • An automatic confirmation to the sender (the autoresponder) needs Professional or above. Webhooks to Slack, Discord or your own endpoint also start at Professional.
  • From Professional, a form automation can email a different colleague depending on the topic chosen.
  • Add a hidden _subject field to set the notification subject line.

Questions

Do I need a consent checkbox on a contact us form?
Usually not for answering the message itself, because replying is what the person asked for. Link your privacy notice near the submit button. Add an unticked checkbox only for something extra, such as marketing email.
How is this different from the simple contact template?
The simple template has three fields for a personal site or a single inbox. This one adds a topic choice so a team can see at a glance who should answer.

Customize before publishing

  • Remove fields you do not need and mark only essential answers as required.
  • Replace placeholder wording with terms your audience already understands.
  • Add your privacy notice, retention details and expected response time.
  • Test keyboard focus, validation errors and the confirmation state on mobile.

Use this template

Create a free account and the form is set up from this template. Copy your endpoint and replace https://forms.formward.eu/f/your-form-id in the code above.

Use this template →

No website? Use a hosted form