On this page
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-nameautocomplete, 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 startdate. - 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.



