How to Create a Signup Form in HTML and CSS

This recipe builds a signup form with name, email, password, and confirm-password fields, plus a small JavaScript check that gives instant feedback when the two password fields don't match.

The Four Fields a Signup Form Needs

A signup form asks for more than a login form because it's creating a record, not just checking one. At minimum that's a name, an email address, a password, and — for usability, not security — a second password field to catch typos before an account gets created with a password the person can't reproduce.

  • Full name — type="text" with autocomplete="name"
  • Email — type="email" with autocomplete="email", required so it can't be skipped
  • Password — type="password" with autocomplete="new-password" and a minlength
  • Confirm password — a second type="password" field, compared to the first with JavaScript

Example

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Sign Up</title>
<style>
  * { box-sizing: border-box; }
  body {
    font-family: system-ui, sans-serif;
    background: #f3f4f6;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    margin: 0;
    padding: 1rem;
  }
  .signup-card {
    background: #fff;
    padding: 2rem;
    border-radius: 12px;
    box-shadow: 0 10px 25px rgba(0,0,0,0.08);
    width: 100%;
    max-width: 400px;
  }
  .signup-card h1 {
    font-size: 1.5rem;
    margin: 0 0 1.5rem;
    text-align: center;
  }
  .field {
    margin-bottom: 1.25rem;
  }
  .field label {
    display: block;
    font-size: 0.875rem;
    font-weight: 600;
    margin-bottom: 0.375rem;
  }
  .field input {
    width: 100%;
    padding: 0.625rem 0.75rem;
    border: 1px solid #d1d5db;
    border-radius: 8px;
    font-size: 1rem;
  }
  .error-text {
    color: #dc2626;
    font-size: 0.8rem;
    margin-top: 0.375rem;
    min-height: 1.1em;
  }
  .submit-btn {
    width: 100%;
    padding: 0.75rem;
    background: #16a34a;
    color: #fff;
    border: none;
    border-radius: 8px;
    font-size: 1rem;
    font-weight: 600;
    cursor: pointer;
  }
  .submit-btn:disabled {
    background: #9ca3af;
    cursor: not-allowed;
  }
</style>
</head>
<body>
  <form class="signup-card" id="signup-form" action="/signup" method="post" novalidate>
    <h1>Create Account</h1>
    <div class="field">
      <label for="signup-name">Full Name</label>
      <input type="text" id="signup-name" name="name" autocomplete="name" required>
    </div>
    <div class="field">
      <label for="signup-email">Email</label>
      <input type="email" id="signup-email" name="email" autocomplete="email" required>
    </div>
    <div class="field">
      <label for="signup-password">Password</label>
      <input type="password" id="signup-password" name="password" autocomplete="new-password" minlength="8" required>
    </div>
    <div class="field">
      <label for="signup-confirm">Confirm Password</label>
      <input type="password" id="signup-confirm" name="confirm-password" autocomplete="new-password" required>
      <div class="error-text" id="confirm-error"></div>
    </div>
    <button type="submit" class="submit-btn" id="signup-submit">Sign Up</button>
  </form>

  <script>
    const form = document.getElementById('signup-form');
    const password = document.getElementById('signup-password');
    const confirm = document.getElementById('signup-confirm');
    const errorText = document.getElementById('confirm-error');

    function passwordsMatch() {
      return password.value.length > 0 && password.value === confirm.value;
    }

    form.addEventListener('submit', (event) => {
      if (!passwordsMatch()) {
        event.preventDefault();
        errorText.textContent = 'Passwords do not match.';
        confirm.focus();
      }
    });

    confirm.addEventListener('input', () => {
      errorText.textContent = passwordsMatch() ? '' : 'Passwords do not match.';
    });
  </script>
</body>
</html>

The confirm-password field exists purely so a person doesn't discover a typo the hard way, by being locked out after account creation. It has no security value on its own — it's just a second box for the same secret, compared against the first before the form is allowed to submit.

Note: Give the password and confirm-password fields different autocomplete values in spirit, but the same value in practice: autocomplete="new-password" on both tells browsers to suggest a generated password rather than autofilling a saved one, which is exactly what you want during account creation.

Checking the Match Client-Side

The script below listens for the form's submit event, compares the two password values, and calls preventDefault() to stop the submission if they differ. It also re-checks on every keystroke in the confirm field, so the error clears the moment the two fields agree instead of waiting for another submit attempt.

  • Listen for input on the confirm field and re-run the comparison on every keystroke
  • Listen for submit on the form and call event.preventDefault() when the values don't match
  • Write the mismatch message into a dedicated element next to the field, not into an alert()
  • Move focus back to the confirm field so the person can fix it immediately
Note: This match check is a UX nicety, not real security. It runs entirely in the visitor's browser, so anyone can disable JavaScript or submit the form directly and skip it. The server that creates the account must independently validate the password (length, complexity, hashing) — never trust that a client-side check ran.
ResponsibilityClient-side JSServer-side
Catching a password typo instantlyYes — this is what it's forToo late, account already exists
Enforcing minimum password strengthOnly as a hint (minlength)Required — the client can't be trusted
Preventing duplicate accountsNot possibleRequired — needs the database
Storing the password safely (hashed)Not possibleRequired, always

Keep the card styling consistent with the rest of your forms — same max-width, same padding, same border-radius — so a visitor moving from login to signup doesn't feel like they've left the site. The only meaningful difference should be the fields themselves and the accent color on the submit button, if you use one to distinguish the action.

Frequently Asked Questions

How do I create a signup form in HTML?
Use a <form> with inputs for name, email and password, each with a <label> and the required attribute. Browsers then block submission and show a message when a field is empty, with no JavaScript involved.
How do I validate a signup form without JavaScript?
Lean on built-in attributes: required, type="email", minlength and pattern. Style the states with the :valid and :invalid pseudo-classes. This covers most rules; only cross-field checks such as password confirmation need script.