Himanshu SharmaSoftware engineer
Let’s talk

HTML · Lesson 26 of 31

Built-in form validation

Use HTML validation attributes such as required, pattern, min, max and minlength to catch mistakes before submit, and learn why servers must validate too.

12 minFormsUpdated 18 October 2026

By the end of this lessonAdd browser-side validation with HTML attributes and clear messages, and explain why the server must check the data again.

Catch mistakes early

Imagine filling in a long form, pressing submit, waiting for the page to reload, and being told you left out your phone number, with everything else gone. HTML includes a built-in way to avoid that: validation attributes that make the browser check the fields before sending, and show a message next to the field that needs fixing. No JavaScript is required.

The main attributes

html
<form>
  <p>
    <label for="name">Name (required)</label><br>
    <input type="text" id="name" name="name" required minlength="2" maxlength="40">
  </p>
  <p>
    <label for="age">Age (18 to 99)</label><br>
    <input type="number" id="age" name="age" min="18" max="99">
  </p>
  <p>
    <label for="mail">Email</label><br>
    <input type="email" id="mail" name="email" required>
  </p>
  <button type="submit">Check</button>
</form>

Try pressing Check with the fields empty or wrong. The browser stops and points to the first problem. (In this sandbox it cannot send the form, but the checking still works.)

  • required: the field must not be empty. For checkboxes, it must be ticked; for radios, one in the group must be chosen.
  • minlength / maxlength: limits on the number of characters.
  • min / max: limits on numbers and dates.
  • step: allowed increments, such as step="5" for multiples of five.
  • type: email, url, number and date each check their own format.

pattern: your own rule

For formats HTML does not know, pattern takes a regular expression that the whole value must match:

html
<form>
  <label for="pin">PIN code (6 digits)</label><br>
  <input type="text" id="pin" name="pin" pattern="[0-9]{6}" inputmode="numeric" title="Enter exactly 6 digits" required>
  <button type="submit">Check</button>
</form>
  • [0-9]{6} means six characters, each a digit.
  • The title text is shown in the error message, so always include it. Say what is expected: "Enter exactly 6 digits".
  • inputmode="numeric" brings up a number keypad on phones while keeping the field a text input (so leading zeros survive).

A few patterns you might see: [A-Za-z ]+ (letters and spaces), .{8,} (at least eight characters). Test patterns carefully; an over-strict pattern turns away real people. Never forbid valid characters in names, for example.

Turning validation off

Add novalidate to the form to switch off the browser's checks, for example when you build your own error messages. A submit button can skip checks for one case with formnovalidate, useful for a "Save draft" button.

Styling valid and invalid fields

CSS can react to validation state with :valid and :invalid:

css
input:invalid { border-color: crimson; }

Be careful: an empty required field is :invalid the moment the page loads, which makes a form look angry before anyone typed. The :user-invalid pseudo-class (supported by current browsers) only applies after the visitor has interacted.

Friendly messages

The browser's default messages are generic and translated into the visitor's language, which is a point in their favour. You can customise them with scripting, but start with a clear label, a hint under the field and a good title on patterns. Do not rely on a coloured border alone.

Never trust the browser

This is the most important idea in the lesson. Validation attributes run in the visitor's browser. A person can open developer tools and remove required, change the pattern, or skip the page and send a request directly with a tool. So:

  • Browser validation is about being helpful to honest visitors.
  • Server validation is about protecting your system, and is always required.

Every value that reaches a server should be checked there for type, length, range and format, and treated as untrusted input.

Common mistakes

  • Only validating in the browser.
  • A pattern with no title, which produces a confusing message.
  • Over-strict rules on names, addresses and phone numbers.
  • Using type="number" for identifiers like PIN codes, losing leading zeros.
  • Red-styling every field on load with :invalid.

Exercise

Create a sign-up form: username (required, 3 to 15 characters, letters and numbers only), password (required, at least 8 characters), and a checkbox "I accept the terms" that must be ticked.

Show one possible answer
html
<form action="/signup" method="post">
  <p>
    <label for="user">Username</label><br>
    <input type="text" id="user" name="username" required minlength="3" maxlength="15" pattern="[A-Za-z0-9]+" title="3 to 15 letters or numbers">
  </p>
  <p>
    <label for="pass">Password</label><br>
    <input type="password" id="pass" name="password" required minlength="8" autocomplete="new-password">
  </p>
  <p>
    <label><input type="checkbox" name="terms" required> I accept the terms</label>
  </p>
  <button type="submit">Create account</button>
</form>

Key points

  • Browsers can check input before the form is sent, using attributes such as required, minlength, maxlength, min, max and pattern.
  • Input types such as email, url and number add their own checks automatically.
  • pattern takes a regular expression, and a title attribute explains the expected format.
  • novalidate on the form turns off browser checks, which is useful when you provide your own.
  • Browser validation is a convenience; the server must always validate again because anyone can bypass the browser.

Check yourself

1. Which attribute stops a form from submitting with an empty field?
2. What does pattern="[0-9]{6}" check?
3. Why must a server validate the data even if the form has validation attributes?
4. Which input type already checks for a valid-looking address without any extra attribute?

Free tools for this lesson