Using Pattern Attribute for Form Input Validation

Modern illustration of a form input field with a glowing validation checkmark pattern overlaid on a clean digital interface background.

The input pattern attribute lets you attach a regular expression directly to an HTML form field, so the browser validates the user's input before the form ever submits. It's one of the most practical tools in HTML5 input validation, and it works without a single line of JavaScript. You write a regex, drop it into the pattern attribute, and the browser does the rest.

What the pattern attribute actually does

When a user submits a form, the browser checks whether the field's value matches the regex you provided in pattern. If it doesn't match, the browser blocks submission and shows a native validation tooltip. No server round-trip, no JavaScript event listener needed.

The pattern is implicitly anchored. That means the regex must match the entire value, not just a substring of it. A pattern of [0-9]{4} will only pass if the field contains exactly four digits and nothing else. This is different from how regex works in most programming languages, where you'd need explicit ^ and $ anchors to get the same behavior.

The pattern attribute is defined in the WHATWG HTML Living Standard and is supported in all modern browsers, including Chrome, Firefox, Safari, and Edge.

Basic syntax and how to write it

Add pattern as an attribute on any <input/> element (except type="hidden", type="range", type="color", type="checkbox", type="radio", type="file", and the button types). The value is a JavaScript-compatible regular expression string, without the surrounding slashes.

<input name="username" pattern="[a-zA-Z0-9_]{3,20}" required="" title="3 to 20 characters: letters, numbers, or underscores" type="text"/>

A few things to keep in mind when writing patterns:

  • No leading or trailing slashes (write [0-9]+, not /[0-9]+/).
  • No flags like i or g . The match is always case-sensitive unless you explicitly include both cases in the character class (e.g., [a-zA-Z]).
  • The whole value must match, so you rarely need ^ or $.
  • Special HTML characters in the regex (like & or <) should be HTML-encoded in the attribute value.

Common regex patterns you can use today

Here are practical, copy-paste-ready patterns for the most common form fields:

Use case Pattern What it allows
US ZIP code [0-9]{5}(-[0-9]{4})? 5 digits, optionally followed by a hyphen and 4 digits
Phone number (international) \+?[0-9\s\-\(\)]{7,20} Optional +, then digits, spaces, hyphens, parens
Username [a-zA-Z0-9_]{3,20} Letters, digits, underscores, 3-20 chars
Strong password (?=.*[A-Z])(?=.*[0-9]).{8,} At least 8 chars, one uppercase, one digit
Date (YYYY-MM-DD) [0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]) ISO 8601 date format
Hex color #[0-9a-fA-F]{6} Six-digit hex color code with leading #
Alphanumeric only [a-zA-Z0-9]+ One or more letters or digits, nothing else

For the password pattern, note that lookaheads like (?=...) are supported in the HTML pattern attribute because it uses JavaScript regex syntax. Browser support for lookaheads in pattern is solid across all modern browsers.

Using title to explain the rule to users

Always pair pattern with a title attribute. When validation fails, most browsers append the title text to their native error tooltip. Without it, users see a generic "Please match the requested format" message that tells them absolutely nothing useful.

<input name="zip" pattern="[0-9]{5}(-[0-9]{4})?" required="" title="Enter a 5-digit ZIP code, like 90210 or 90210-1234" type="text"/>

Chrome shows the tooltip as "Please match the requested format: [your title]". Firefox shows just the title text. Either way, a clear, human-readable description dramatically reduces user frustration. Think of title as the inline error message for your regex rule.

Accessibility note: The native tooltip shown on validation failure is not reliably announced by screen readers. For accessible forms, also add a visible hint below the field using aria-describedby pointing to a <span> </span> that describes the expected format.

Pattern vs. type: when to use which

HTML5 already gives you several type values that handle validation automatically: type="email", type="url", type="number", type="tel", and others. The pattern attribute fills the gap when the built-in types aren't specific enough.

  • Use type="email" for email addresses. It validates basic email format natively and is better supported by mobile keyboards and autofill.
  • Use type="tel" plus pattern for phone numbers. The tel type brings up a numeric keyboard on mobile but applies no format validation on its own, so a pattern like \+?[0-9\s\-\(\)]{7,20} does the actual checking.
  • Use type="text" plus pattern for anything with a custom format: product codes, license plates, postal codes, usernames, and so on.
  • Use type="password" plus pattern to enforce password complexity rules.

You can also combine pattern with other constraint attributes. For example, minlength /maxlength handle length limits more clearly than embedding {min,max} quantifiers in a regex, and required handles the empty-field case so your pattern doesn't need to account for it.

What client-side validation cannot do

This is the part that trips up a lot of developers. The html pattern attribute is purely a client-side validation mechanism. A user can open browser DevTools, remove the pattern attribute, and submit anything they want. Or they can send a raw HTTP request directly to your form's endpoint, bypassing the browser entirely.

This means client-side validation is a UX feature, not a security feature. Use it to give users immediate feedback and reduce bad submissions. Never use it as your only line of defense.

Always validate on the server side too. If your form collects sensitive data or triggers any server action, the server must independently validate every field. Client-side checks are easily bypassed. For a deeper look at keeping form submissions secure, see our guide on securing form submissions against CSRF and other attacks.

A practical approach is to think of the two layers as having different jobs. The pattern attribute catches honest mistakes instantly, before a network request is made. Server-side validation catches everything else, including malicious input and programmatic submissions.

Styling valid and invalid states with CSS

CSS gives you two pseudo-classes that work directly with the constraint validation API that powers the pattern attribute:

  • :valid matches when the current value satisfies all constraints (pattern, required, type, etc.).
  • :invalid matches when any constraint fails.
input:invalid {
  border-color: #e74c3c;
  outline-color: #e74c3c;
}

input:valid {
  border-color: #2ecc71;
}

One common problem: :invalid applies immediately on page load for required fields that are empty, making the form look like it has errors before the user has typed anything. The fix is to use the :user-invalid pseudo-class (supported in Firefox and Chrome 119+), or to scope the styles with a class you add via JavaScript after the first interaction.

/* Only show invalid styling after the user has interacted */
input:user-invalid {
  border-color: #e74c3c;
}

For a complete picture of how HTML form attributes work together, including how the form's action attribute connects to where validated data gets sent, the guide on what the HTML form action attribute actually does is a solid next read.

The MDN reference for the pattern attribute is the most complete documentation available, including edge cases around which input types support it and how the implicit anchoring works in different browser engines.

HTML form with input pattern validation connected to a form backend

Your validated form still needs somewhere to send submissions

Once your input pattern attribute rules are in place, the next step is handling what happens after a valid submission. SendForm receives your HTML form's POST, forwards it to your email, and works on any static site with no backend required.

Try SendForm Free →

No. The pattern attribute only applies to type="text" , type="search" , type="url" , type="tel" , type="email" , and type="password" . It has no effect on type="number" , type="range" , type="date" , type="checkbox" , type="radio" , type="file" , or any button type. For numeric ranges, use the min and max attributes instead.

Not with a flag. The pattern attribute does not support regex flags like i . To match both cases, include both ranges explicitly in a character class. For example, use [a-zA-Z]+ instead of [a-z]+ with an i flag. This is a known limitation of the HTML pattern attribute specification, and there is no workaround other than writing the pattern to cover both cases explicitly.

The browser's built-in validation only runs on a native form submit event triggered through the UI. If you call form.submit() programmatically, or send data via fetch() or XMLHttpRequest , the pattern constraint is not checked. You can manually trigger validation with form.reportValidity() before submitting programmatically, but server-side validation remains essential regardless.

For most cases, just use type="email" instead. It applies a built-in email format check that is well-tested and handles edge cases better than a hand-written regex. If you need a stricter rule, for example limiting to a specific domain, you can combine type="email" with a pattern like .+@yourcompany\.com . Both constraints must pass for the field to be valid.

No. The pattern attribute is a client-side check that any user can bypass by editing the DOM or sending a raw HTTP request. It is a usability tool, not a security control. To protect against malicious input, SQL injection, XSS, or spam, you must validate and sanitize data on the server side as well. Client-side and server-side validation serve different purposes and both are necessary.

Yes. The pattern attribute uses JavaScript's regex engine, so lookaheads ( (?=...) ), non-capturing groups ( (?:...) ), and other advanced features work in all modern browsers. A common use is enforcing password complexity with multiple lookaheads, for example (?=.*[A-Z])(?=.*[0-9]).{8,} to require at least one uppercase letter and one digit in a minimum 8-character password.