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.
Content Table
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.
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
iorg. 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.
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"pluspatternfor phone numbers. Theteltype 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"pluspatternfor anything with a custom format: product codes, license plates, postal codes, usernames, and so on. -
Use
type="password"pluspatternto 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.
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:
-
:validmatches when the current value satisfies all constraints (pattern, required, type, etc.). -
:invalidmatches 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.
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.