Form validation

3 snippets across 3 stacks — HTML & CSS, React, Regular Expressions

HCHTML & CSS

Form Validation Attributes

HC · Forms
syntax
required | minlength="n" | maxlength="n" | pattern="regex" | min="n" | max="n"
example
<input type="text" name="zipcode" required pattern="[0-9]{5}" title="Five digit zip code">

<input type="password" name="password" required minlength="8" maxlength="128">

<input type="number" name="age" min="18" max="120">

Note Built-in validation runs on form submission and is bypassed by adding formnovalidate to the submit button or novalidate to the form. The title attribute provides a hint message shown when the pattern fails.

REReact

Validation Patterns

RE · Forms
syntax
// Validate on change, blur, or submit
const errors = {};
if (!value) errors.field = 'Required';
example
function SignupForm() {
  const [fields, setFields] = useState({ name: '', email: '', age: '' });
  const [errors, setErrors] = useState({});
  const [touched, setTouched] = useState({});

  function validate(data) {
    const errs = {};
    if (!data.name.trim()) errs.name = 'Name is required';
    if (!data.email.includes('@')) errs.email = 'Invalid email';
    if (Number(data.age) < 18) errs.age = 'Must be at least 18';
    return errs;
  }

  function handleChange(field, value) {
    const updated = { ...fields, [field]: value };
    setFields(updated);
    if (touched[field]) {
      setErrors(validate(updated));
    }
  }

  function handleBlur(field) {
    setTouched(prev => ({ ...prev, [field]: true }));
    setErrors(validate(fields));
  }

  function handleSubmit(e) {
    e.preventDefault();
    const errs = validate(fields);
    setErrors(errs);
    setTouched({ name: true, email: true, age: true });
    if (Object.keys(errs).length === 0) {
      submitSignup(fields);
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={fields.name}
        onChange={e => handleChange('name', e.target.value)}
        onBlur={() => handleBlur('name')}
      />
      {touched.name && errors.name && <span>{errors.name}</span>}
      {/* repeat for other fields */}
      <button type="submit">Sign Up</button>
    </form>
  );
}

Note Validate on blur for a good UX: users see errors after leaving a field, not while typing. On submit, validate everything and mark all fields as touched. For complex forms, consider a form library or useReducer.

RXRegular Expressions

Stacked Lookaheads for Validation

RX · Lookahead & Lookbehind
syntax
^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[!@#$%]).{8,}$
example
JS:  const strong = /^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[!@#$%]).{8,}$/
     strong.test('MyP@ss1word')   // true
     strong.test('weakpass')       // false
Py:  pattern = r'^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[!@#$%]).{8,}$'
     bool(re.match(pattern, 'MyP@ss1word'))  # True
output
true / True for 'MyP@ss1word', false / False for 'weakpass'

Note Each lookahead checks a different requirement without advancing the match position. All must pass before .{8,} consumes the string. This is a widely-used pattern but be cautious about catastrophic backtracking with very long inputs -- consider checking each requirement separately in code for production validation.