pattern

Native form validation attributes that reduce JavaScript. Covers pattern, min/max/step, minlength/maxlength, required, validation pseudo-classes, and form association.

Overview

HTML provides a family of attributes for client-side form validation that work without any JavaScript. The browser checks these constraints on submit and shows native error messages when values do not match.

  • pattern validates against a regular expression
  • min, max, step constrain numeric and date values
  • minlength, maxlength constrain text length
  • required prevents empty submissions

These attributes complement each other. Use them together for robust validation that works even when JavaScript fails.

Applies to: <input>, <select>, <textarea>

pattern

The pattern attribute specifies a regular expression that the input value must match for the form to submit. The regex is matched against the entire value (anchored with implicit ^ and $), so you do not need to add anchors.

Always pair pattern with a title attribute that describes the expected format in plain language. The browser includes the title text in the validation error tooltip.

PatternPurposeTitle text
[0-9]{5}US ZIP code"Five-digit ZIP code"
[0-9]{5}(-[0-9]{4})?ZIP+4"ZIP code (12345 or 12345-6789)"
[0-9]{3}-[0-9]{3}-[0-9]{4}US phone"Format: 123-456-7890"
[a-zA-Z][a-zA-Z0-9_-]{2,15}Username"3-16 characters, starts with a letter"
[A-Z]{2}[0-9]{6}Document ID"Two uppercase letters followed by six digits"
#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})Hex color"Hex color (e.g., #ff0 or #ff0000)"
<form class="stacked"> <label for="zip">ZIP Code</label> <input type="text" id="zip" pattern="[0-9]{5}" title="Five-digit ZIP code" required /> <label for="phone">Phone Number</label> <input type="tel" id="phone" pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}" title="Format: 123-456-7890" placeholder="123-456-7890" /> <label for="username">Username</label> <input type="text" id="username" pattern="[a-zA-Z][a-zA-Z0-9_-]{2,15}" title="3-16 characters, starting with a letter. Letters, numbers, hyphens, and underscores only." required /> <button type="submit">Submit</button> </form>

Try submitting with invalid values to see native validation messages.

Pattern Tips

  • The pattern is implicitly anchored: [0-9]{5} means ^[0-9]{5}$.
  • Patterns are only checked when the field has a value. An empty optional field with a pattern is valid. Add required if the field must be filled.
  • pattern works on text, tel, email, url, password, and search input types. It does not apply to number, date, or range types.

min, max, step

These attributes constrain numeric, date, time, and range inputs. The browser rejects values outside the range or misaligned with the step.

AttributePurposeApplies to
minMinimum allowed valuenumber, range, date, time, datetime-local, month, week
maxMaximum allowed valueSame as min
stepLegal increment from the min valueSame as min
<form class="stacked"> <label for="qty">Quantity (1-10)</label> <input type="number" id="qty" min="1" max="10" step="1" value="1" /> <label for="price">Price (increments of 0.01)</label> <input type="number" id="price" min="0" max="9999.99" step="0.01" /> <label for="volume">Volume</label> <input type="range" id="volume" min="0" max="100" step="5" value="50" /> <label for="appt">Appointment Date</label> <input type="date" id="appt" min="2025-01-01" max="2025-12-31" /> <label for="meeting">Meeting Time</label> <input type="time" id="meeting" min="09:00" max="17:00" step="900" /> <button type="submit">Submit</button> </form>

Step Mismatch

If a value does not align with the step, the browser shows a "step mismatch" error on submit. For example, with min="0" step="5", the value 3 is invalid because it is not a multiple of 5 from the minimum. Use step="any" to accept any value within the min/max range.

minlength and maxlength

These attributes constrain the number of characters in text inputs and textareas.

AttributeBehavior
maxlengthPrevents typing beyond the limit. The browser silently stops accepting characters.
minlengthValidates on submit. The browser shows an error if the value is too short.

Applies to: <input> (text types), <textarea>

<form class="stacked"> <label for="tweet">Short Message (max 280 characters)</label> <textarea id="tweet" maxlength="280" rows="3"></textarea> <label for="pw">Password (8-64 characters)</label> <input type="password" id="pw" minlength="8" maxlength="64" required /> <label for="code">Invite Code (exactly 6 characters)</label> <input type="text" id="code" minlength="6" maxlength="6" required /> <button type="submit">Submit</button> </form>

Visible Character Counter

The VB data-count attribute adds a visible "X / Y" character counter when used alongside maxlength. No JavaScript setup required.

<form-field> <label for="bio">Bio</label> <textarea id="bio" maxlength="200" data-count rows="3"></textarea> </form-field>

required

The required attribute prevents form submission when a field is empty. It is part of the same constraint validation system as pattern and min/max.

<form class="stacked"> <label for="email">Email <span aria-hidden="true">*</span></label> <input type="email" id="email" required /> <label for="name">Full Name <span aria-hidden="true">*</span></label> <input type="text" id="name" required /> <label for="notes">Notes (optional)</label> <textarea id="notes" rows="3"></textarea> <button type="submit">Submit</button> </form>

Applies to: <input>, <select>, <textarea>

For checkboxes, required means the checkbox must be checked. For radio buttons, at least one radio in the group must be selected. For <select>, the first option must have an empty value (value="") to serve as the placeholder.

Validation Pseudo-Classes

CSS pseudo-classes let you style fields based on their validation state. No JavaScript needed.

Pseudo-classMatches when
:validThe field's value satisfies all constraints
:invalidThe field's value violates a constraint
:user-validValid, and the user has interacted with the field
:user-invalidInvalid, and the user has interacted with the field
:requiredThe field has the required attribute
:optionalThe field does not have required
:in-rangeThe value is within min/max bounds
:out-of-rangeThe value is outside min/max bounds
/* Turns green border when valid */ input:valid { border-color: var(--color-success); } /* Turns red border when invalid */ input:invalid { border-color: var(--color-danger); } /* Only shows invalid styles AFTER user interaction */ input:user-invalid { border-color: var(--color-danger); outline-color: var(--color-danger); } /* Style the submit button when the entire form is valid */ form:valid button[type="submit"] { opacity: 1; } form:invalid button[type="submit"] { opacity: 0.5; }

Prefer :user-invalid Over :invalid

The :invalid pseudo-class applies immediately on page load, which means required fields show error styles before the user has done anything. Use :user-invalid instead to only show error styles after the user has interacted with the field (typed, blurred, or submitted). This provides a much better user experience.

form and formaction

Two association attributes let you break out of normal form boundaries.

The form Attribute

The form attribute associates a control with a <form> element elsewhere on the page by referencing its id. The control does not need to be a descendant of the form.

<!-- Input associated with a form elsewhere on the page --> <form id="search-form" action="/search" method="get"> <label for="q">Search</label> <input type="search" id="q" name="q" /> </form> <!-- This button is outside the form but submits it --> <button type="submit" form="search-form">Search</button> <!-- This input is outside the form but belongs to it --> <input type="hidden" name="source" value="header" form="search-form" />

The formaction Attribute

The formaction attribute on a submit button overrides the form's action URL for that specific submission. Similarly, formmethod, formenctype, and formnovalidate override their counterparts on the form element.

Button attributeOverrides
formactionThe form's action URL
formmethodThe form's method (GET/POST)
formenctypeThe form's enctype
formnovalidateSkips constraint validation for this submission
<form action="/save" method="post" class="stacked"> <label for="doc">Document</label> <textarea id="doc" name="content" rows="4"></textarea> <!-- Default action: /save --> <footer class="actions end"> <button type="submit">Save</button> <!-- Override: submit to /save-draft instead --> <button type="submit" formaction="/save-draft">Save as Draft</button> <!-- Override: submit to /preview with GET --> <button type="submit" formaction="/preview" formmethod="get">Preview</button> </footer> </form>

Combining Constraints

Validation attributes work together. A single input can use required, pattern, minlength, and maxlength simultaneously. The browser checks all constraints and reports the first failure.

<form class="stacked"> <label for="reg-user">Username</label> <input type="text" id="reg-user" pattern="[a-zA-Z][a-zA-Z0-9_]{2,19}" title="3-20 characters, starting with a letter" minlength="3" maxlength="20" required /> <label for="reg-email">Email</label> <input type="email" id="reg-email" required /> <label for="reg-pw">Password</label> <input type="password" id="reg-pw" minlength="8" maxlength="128" pattern="(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).{8,}" title="At least 8 characters with one uppercase letter, one lowercase letter, and one number" required /> <label for="reg-age">Age</label> <input type="number" id="reg-age" min="13" max="150" required /> <button type="submit">Create Account</button> </form>

Try submitting to see how multiple constraints are validated together.

See Also