Ad – 728Γ—90
🌐 Forms

HTML Form Validation – required, pattern, min, max, minlength

HTML5 built-in form validation lets browsers check user input before the form is submitted β€” without any JavaScript. Using attributes like required, pattern, minlength, and min/max, you can catch common errors instantly and guide users to submit correct data.

⏱️ 15 min read🎯 BeginnerπŸ“… Updated 2026

Why Validation Matters

Validation serves two purposes:

  • User experience β€” giving immediate feedback prevents frustration from submitting a form only to find out the email was missing the @ symbol.
  • Data quality β€” ensuring data arrives in the format your server or database expects, reducing errors and security risks.

HTML5 provides basic validation for free. For complex rules or security-critical checks, you supplement it with JavaScript and always repeat validation server-side.

The required Attribute

Adding required to a field means the browser will refuse to submit the form if that field is empty. It works on input, textarea, and select.

HTML
<form action="/subscribe" method="POST">

  <label for="sub-email">Email address *</label>
  <input type="email" id="sub-email" name="email" required>

  <label for="sub-name">Full name *</label>
  <input type="text" id="sub-name" name="name" required>

  <!-- Optional field – no required attribute -->
  <label for="sub-company">Company (optional)</label>
  <input type="text" id="sub-company" name="company">

  <button type="submit">Subscribe</button>
</form>

minlength and maxlength

These attributes constrain the number of characters in a text-based input or textarea.

HTML
<!-- Password: at least 8, no more than 64 characters -->
<label for="pw">Password</label>
<input type="password" id="pw" name="password"
       minlength="8" maxlength="64" required>

<!-- Username: 3–20 characters -->
<label for="uname">Username</label>
<input type="text" id="uname" name="username"
       minlength="3" maxlength="20" required>

<!-- Textarea: bio up to 500 characters -->
<label for="bio">Bio (max 500 characters)</label>
<textarea id="bio" name="bio" rows="4" maxlength="500"></textarea>
maxlength silently truncates input as the user types β€” the browser prevents entering more characters. minlength is only checked on submit; the browser does not prevent typing fewer characters than the minimum.

min, max, step for Numbers and Dates

For type="number", type="range", and date/time inputs, use min, max, and step to constrain the allowed range.

HTML
<!-- Age: integer between 16 and 120 -->
<label for="age">Age</label>
<input type="number" id="age" name="age" min="16" max="120" step="1" required>

<!-- Price: dollars and cents, 0.00 to 9999.99 -->
<label for="price">Price (USD)</label>
<input type="number" id="price" name="price" min="0" max="9999.99" step="0.01">

<!-- Event date: must be in 2026 -->
<label for="event-date">Event date</label>
<input type="date" id="event-date" name="event_date"
       min="2026-01-01" max="2026-12-31" required>

<!-- Working hours: 9am to 5pm, in 30-min slots -->
<label for="appt-time">Appointment time</label>
<input type="time" id="appt-time" name="time"
       min="09:00" max="17:00" step="1800">
Ad – 336Γ—280

The pattern Attribute with Regex

The pattern attribute accepts a JavaScript-compatible regular expression. The entire input value must match the pattern for the form to submit. This is essential for type="tel" (which has no built-in format validation) and for custom text formats.

HTML
<!-- UK phone number: starts with 07 or +447, then 9 digits -->
<label for="uk-phone">UK mobile number</label>
<input type="tel" id="uk-phone" name="phone"
       pattern="(07\d{9}|\+447\d{9})"
       placeholder="07700 900000"
       title="Enter a UK mobile number: 07xxxxxxxxx or +447xxxxxxxxx"
       required>

<!-- UK postcode: letter(s) + digit(s) + space + digit + two letters -->
<label for="postcode">Postcode</label>
<input type="text" id="postcode" name="postcode"
       pattern="[A-Z]{1,2}[0-9][0-9A-Z]?\s?[0-9][A-Z]{2}"
       placeholder="SW1A 1AA"
       title="Enter a valid UK postcode in uppercase"
       required>

<!-- Hex colour code: # followed by exactly 6 hex digits -->
<label for="hex-color">Hex colour</label>
<input type="text" id="hex-color" name="color"
       pattern="#[0-9A-Fa-f]{6}"
       placeholder="#ff5733"
       title="Enter a hex colour code like #ff5733">

<!-- Username: letters, numbers, underscores, 3-20 chars -->
<label for="handle">Username</label>
<input type="text" id="handle" name="username"
       pattern="[a-zA-Z0-9_]{3,20}"
       title="3 to 20 characters: letters, numbers, and underscores only"
       required>
Always include a title attribute alongside pattern. The browser shows this text in the validation tooltip, telling users what format is expected. Without it, the error message is generic and unhelpful.

Type Validation

Input types email and url have built-in format validation β€” no pattern needed for basic checking.

HTML
<!-- Browser validates: must contain @ and a domain part -->
<input type="email" name="email" required>

<!-- Browser validates: must start with http:// or https:// -->
<input type="url" name="website">

<!-- Number type rejects non-numeric characters -->
<input type="number" name="quantity" min="1">

The novalidate Attribute

Adding novalidate to the <form> element disables all browser-native validation for that form. This is useful when you want to handle validation entirely with JavaScript and display your own custom error messages.

HTML
<!-- Browser validation disabled – JavaScript takes over -->
<form action="/register" method="POST" novalidate id="register-form">
  <label for="reg-email">Email</label>
  <input type="email" id="reg-email" name="email" required>
  <div class="error-message" id="email-error" role="alert"></div>

  <button type="submit">Create account</button>
</form>
JavaScript
document.getElementById('register-form').addEventListener('submit', function(e) {
  e.preventDefault();
  const emailInput = document.getElementById('reg-email');
  const errorDiv   = document.getElementById('email-error');

  if (!emailInput.validity.valid) {
    errorDiv.textContent = 'Please enter a valid email address.';
    emailInput.focus();
    return;
  }
  errorDiv.textContent = '';
  // proceed with form submission
  this.submit();
});

Customising Validation Messages with setCustomValidity()

The Constraint Validation API lets you set a custom error message that the browser displays in its native tooltip. Call setCustomValidity('') to clear the error (mark the field valid again).

HTML
<label for="confirm-pw">Confirm password</label>
<input type="password" id="confirm-pw" name="confirm_password" required>
JavaScript
const pw        = document.getElementById('pw');
const confirmPw = document.getElementById('confirm-pw');

confirmPw.addEventListener('input', function() {
  if (this.value !== pw.value) {
    this.setCustomValidity('Passwords do not match.');
  } else {
    this.setCustomValidity(''); // clear error β†’ field is valid
  }
});

Client-Side vs Server-Side Validation

Browser validation is a convenience β€” it is never a security measure on its own.

Client-side (HTML/JS)Server-side
SpeedInstant, no round-tripRequires network request
Can be bypassed?Yes β€” easily disabled in DevTools or via API callsNo β€” server always runs the check
Can access database?NoYes β€” can check uniqueness, existing records
SecurityNever trust aloneRequired for all security-sensitive rules
Use forImmediate UX feedback (empty fields, format hints)All business logic, unique email, passwords, authorisation
Always validate server-side as well. A malicious user can disable JavaScript, edit HTML in DevTools, or send HTTP requests directly with tools like curl β€” bypassing all browser validation. Treat every incoming form submission as untrusted data.

πŸ“‹ Summary

  • required β€” prevents submission if the field is empty.
  • minlength / maxlength β€” constrains the character count for text inputs and textareas.
  • min / max / step β€” constrains numeric and date input values.
  • pattern β€” validates input against a regex; always add a descriptive title.
  • type="email" and type="url" perform automatic format validation.
  • novalidate on the form disables native browser validation β€” use when writing custom JS validation.
  • setCustomValidity() customises the browser's native validation error message.
  • Client-side validation is for UX; server-side validation is for security β€” you need both.

Frequently Asked Questions

Does the required attribute work on checkboxes?

Yes. On a checkbox, required means the box must be checked before the form submits. This is commonly used for "I agree to the terms" checkboxes. For a group of checkboxes where at least one must be selected, you need JavaScript β€” HTML cannot express that constraint natively.

Why does my pattern validation fail even though the value looks correct?

The pattern attribute implicitly anchors the regex to match the entire value (like wrapping your pattern in ^...$). So a pattern of [0-9]{5} requires the entire field to be exactly 5 digits β€” not just contain 5 digits somewhere. If your value has extra characters, the match fails. Also check that you haven't used regex delimiters (/pattern/) β€” the pattern attribute only takes the pattern string, no slashes.

Can I show custom styled error messages instead of the browser tooltip?

Yes. Add novalidate to the form, prevent default submission in a JavaScript event listener, check element.validity properties, and display your own error elements. The validity object exposes booleans like valueMissing, typeMismatch, patternMismatch, tooShort, and rangeUnderflow so you can tailor each message.

What is the maxlength limit on a textarea?

There is no browser-imposed upper limit on the maxlength value itself β€” you choose the number. A practical guideline: match it to your database column size to prevent truncation errors. For a VARCHAR(255) column, set maxlength="255". For long-form content stored in a TEXT column, you might set 5000 or omit maxlength entirely and rely on server-side trimming.