Formclick

How to Create a Registration Form in HTML

Build an event or membership registration form in HTML and CSS — with grouped sections, smart validation, a terms checkbox and submissions delivered straight to your inbox.

FFormclick Team4 min read
Close-up of hands arranging name tags on a conference table, preparing for a corporate event.
Photo by RDNE Stock project on Pexels
On this page
  1. Plan your fields first
  2. The HTML
  3. The CSS
  4. Validation tips for registration forms
  5. What about account sign-up with passwords?
  6. Adapting the form

Registration forms collect the details you need to sign someone up — for an event, a course, a club, a beta programme or a membership. They are longer than a contact form, so structure and validation matter a lot more.

In this tutorial we'll build a clean, accessible registration form for an event, then look at how to adapt it for other uses.

Plan your fields first

Before writing code, list every field and ask: do we actually need this now? Each extra field costs completions. For an event, a sensible set is:

  • About you — first name, last name, email, phone (optional)
  • Your ticket — ticket type, dietary requirements
  • Consent — agree to terms, optional marketing opt-in

Anything you can collect later (t-shirt size, company address) should wait.

The HTML

<form class="register" action="https://api.formclick.io/submit" method="POST">
  <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY">
  <input type="hidden" name="subject" value="New event registration">
  <input type="checkbox" name="botcheck" class="hp" tabindex="-1" autocomplete="off">

  <h1>Register for DevDay 2026</h1>
  <p class="intro">Takes about a minute. Fields marked * are required.</p>

  <fieldset>
    <legend>About you</legend>
    <div class="grid">
      <div class="field">
        <label for="first">First name *</label>
        <input id="first" name="first_name" autocomplete="given-name" required>
      </div>
      <div class="field">
        <label for="last">Last name *</label>
        <input id="last" name="last_name" autocomplete="family-name" required>
      </div>
    </div>
    <div class="field">
      <label for="email">Email *</label>
      <input id="email" name="email" type="email" autocomplete="email" required>
      <small>We'll send your ticket here.</small>
    </div>
    <div class="field">
      <label for="phone">Phone</label>
      <input id="phone" name="phone" type="tel" autocomplete="tel">
    </div>
  </fieldset>

  <fieldset>
    <legend>Your ticket</legend>
    <div class="choices">
      <label class="choice">
        <input type="radio" name="ticket" value="standard" required>
        <span><strong>Standard</strong> — talks and lunch</span>
      </label>
      <label class="choice">
        <input type="radio" name="ticket" value="workshop">
        <span><strong>Workshop</strong> — talks, lunch and hands-on sessions</span>
      </label>
    </div>
    <div class="field">
      <label for="diet">Dietary requirements</label>
      <select id="diet" name="dietary">
        <option value="none">None</option>
        <option>Vegetarian</option>
        <option>Vegan</option>
        <option>Gluten-free</option>
        <option>Other (we'll contact you)</option>
      </select>
    </div>
  </fieldset>

  <fieldset>
    <legend>Almost done</legend>
    <label class="check">
      <input type="checkbox" name="accept_terms" value="yes" required>
      I agree to the <a href="/terms" target="_blank">event terms</a> *
    </label>
    <label class="check">
      <input type="checkbox" name="updates_opt_in" value="yes">
      Email me about future events
    </label>
  </fieldset>

  <button type="submit">Complete registration</button>
</form>

<script src="https://cdn.formclick.io/formclick.min.js"></script>

Why it's built this way

  • Fieldsets with legends break a long form into digestible chunks and tell screen-reader users which section they're in.
  • Radio buttons for ticket type — with only two options, they're faster than a dropdown and show every choice at once.
  • Separate first/last name with given-name/family-name autocomplete, so browsers fill them in correctly.
  • Helper text under the email explains why you need it, which improves trust.
  • Two separate checkboxes — one required (terms), one optional (marketing). Never bundle consent.

The CSS

.register {
  max-width: 620px;
  margin: 40px auto;
  padding: 36px;
  background: #fff;
  border: 1px solid #e6e6f0;
  border-radius: 20px;
  font-family: system-ui, sans-serif;
}
.register h1 { margin: 0 0 4px; font-size: 28px; }
.intro { margin: 0 0 24px; color: #64748b; }

fieldset {
  border: 0;
  padding: 0;
  margin: 0 0 28px;
}
legend {
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: #645cec;
  margin-bottom: 14px;
}
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.field { margin-bottom: 16px; }
.field label { display: block; font-weight: 600; margin-bottom: 6px; }
.field small { display: block; margin-top: 6px; color: #64748b; }

input[type="text"], input:not([type]), input[type="email"],
input[type="tel"], select {
  width: 100%;
  padding: 12px 14px;
  font: inherit;
  border: 1px solid #d4d4e0;
  border-radius: 10px;
}
input:focus, select:focus {
  outline: none;
  border-color: #645cec;
  box-shadow: 0 0 0 3px rgba(100, 92, 236, .2);
}

.choices { display: grid; gap: 10px; margin-bottom: 16px; }
.choice {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 14px 16px;
  border: 1px solid #d4d4e0;
  border-radius: 12px;
  cursor: pointer;
}
.choice:has(input:checked) {
  border-color: #645cec;
  background: #f6f5ff;
}
.check { display: flex; gap: 10px; margin-bottom: 10px; }

button {
  width: 100%;
  padding: 15px;
  font: inherit;
  font-weight: 700;
  color: #fff;
  background: #645cec;
  border: 0;
  border-radius: 12px;
  cursor: pointer;
}
.hp { display: none; }

@media (max-width: 600px) {
  .register { padding: 24px; }
  .grid { grid-template-columns: 1fr; gap: 0; }
}

The :has(input:checked) selector highlights the selected ticket card — no JavaScript needed. It's supported in all modern browsers.

Validation tips for registration forms

  • Don't ask people to type their email twice. It annoys everyone and catches few errors. Send a confirmation email instead.
  • Validate on submit, then live. Showing errors while someone is still typing their first character feels hostile.
  • Make error messages specific: "Enter an email like name@example.com", not "Invalid input".
  • Mark required fields clearly, or — if most are required — mark the optional ones instead.

What about account sign-up with passwords?

If your registration creates a user account (with a password people will log in with later), the form itself looks similar, but the backend is different: it has to hash and store the password and manage sessions. That's the job of your application or an authentication provider, not a form backend. We explain this in How to Create a Login Form in HTML.

For registrations that simply need collecting — events, waitlists, memberships, course enrolments, volunteer sign-ups — a form backend like Formclick is ideal: every registration lands in your inbox and dashboard, and you can export them or send them to Google Sheets.

Adapting the form

  • Course enrolment: swap tickets for a course <select> and add a start date.
  • Club or gym membership: add date of birth and an emergency contact fieldset.
  • Long forms: split the fieldsets into steps — see How to Create a Multi-Step HTML Form.

Want a head start? Browse our registration form template, or variations for membership applications and club sign-ups.

Found this useful? Share it.

All articles