HTML
02 / 12

Forms & Input Types

Forms & Input Types

HTML5 forms have powerful built-in validation, diverse input types, and accessibility features. Using the right input type gives you free mobile keyboard optimization, native UX, and client-side validation without JavaScript.

Form Structure & Attributes

<form
  action="/api/register"
  method="POST"
  enctype="multipart/form-data"   <!-- Required for file uploads -->
  novalidate                        <!-- Disable browser validation (use your own) -->
  autocomplete="on"
  aria-label="User registration form"
>
  <!-- fieldset groups related inputs; legend labels the group -->
  <fieldset>
    <legend>Personal Information</legend>

    <!-- label must reference input via for/id OR wrap the input -->
    <label for="full-name">Full Name <span aria-hidden="true">*</span></label>
    <input
      type="text"
      id="full-name"
      name="fullName"
      required
      minlength="2"
      maxlength="100"
      autocomplete="name"
      placeholder="Jane Doe"
      aria-required="true"
      aria-describedby="name-hint name-error"
    >
    <span id="name-hint" class="hint">First and last name</span>
    <span id="name-error" role="alert" class="error" hidden>Name is required</span>
  </fieldset>

  <!-- Submit button -->
  <button type="submit">Create Account</button>
  <button type="reset">Clear Form</button>
  <button type="button" id="preview-btn">Preview</button>
</form>

All Input Types

<!-- Text inputs -->
<input type="text"     name="username">            <!-- Plain text -->
<input type="email"    name="email" multiple>      <!-- Validates email format; "email" keyboard on mobile -->
<input type="password" name="pwd" autocomplete="new-password">
<input type="search"   name="q">                  <!-- With clear button on some browsers -->
<input type="url"      name="website">             <!-- Validates URL format -->
<input type="tel"      name="phone" pattern="[0-9]{10}">  <!-- Numeric keyboard on mobile -->

<!-- Numeric -->
<input type="number"   name="qty"   min="1" max="100" step="1">
<input type="range"    name="vol"   min="0" max="100" step="5" value="50">

<!-- Date/Time -->
<input type="date"           name="birthday"  min="1900-01-01" max="2025-12-31">
<input type="time"           name="start"     min="09:00" max="17:00" step="900">  <!-- 15min steps -->
<input type="datetime-local" name="event">    <!-- Date + time, no timezone -->
<input type="month"          name="exp">      <!-- YYYY-MM -->
<input type="week"           name="week">     <!-- YYYY-W## -->

<!-- Selectors -->
<input type="color"    name="brand-color" value="#ff6600">
<input type="file"     name="avatar" accept="image/png,image/jpeg" multiple>

<!-- Checkboxes & Radios -->
<input type="checkbox" name="agree"   id="agree"   required>
<input type="radio"    name="plan"    value="free"  checked>
<input type="radio"    name="plan"    value="pro">

<!-- Hidden & Submit -->
<input type="hidden"   name="csrf_token" value="abc123">
<input type="submit"   value="Submit">
<input type="image"    src="/btn.png" alt="Submit order">  <!-- Image submit button -->
<input type="reset">

Validation Attributes

<!-- Built-in validation - no JS needed -->
<input
  type="email"
  required                              <!-- Must not be empty -->
  minlength="5"                         <!-- Min character count -->
  maxlength="254"                       <!-- Max character count -->
  pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$"  <!-- Regex pattern -->
  title="Please enter a valid email address"  <!-- Shown in browser tooltip on invalid -->
>

<input type="number" min="0" max="999" step="0.01">  <!-- Numeric range + increment -->
<input type="file" accept=".pdf,.doc,.docx" multiple> <!-- File type filter -->
<input type="url" pattern="https://.*">              <!-- HTTPS only -->

<!-- Custom validation message via JS -->
<input type="email" id="email" oninvalid="this.setCustomValidity('Enter a valid email')" oninput="this.setCustomValidity('')">

<!-- Constraint Validation API -->
<script>
  const form = document.querySelector('form');
  form.addEventListener('submit', e => {
    if (!form.checkValidity()) {
      e.preventDefault();
      // reportValidity() shows browser tooltips
      form.reportValidity();
    }
  });

  // Check individual input
  const email = document.getElementById('email');
  console.log(email.validity.typeMismatch);  // true if wrong type
  console.log(email.validity.valueMissing);  // true if required + empty
  console.log(email.validationMessage);      // browser message string
</script>

Select, Textarea, Datalist

<!-- Select dropdown -->
<label for="country">Country</label>
<select id="country" name="country" required autocomplete="country">
  <option value="">-- Select a country --</option>
  <optgroup label="North America">
    <option value="US">United States</option>
    <option value="CA">Canada</option>
    <option value="MX">Mexico</option>
  </optgroup>
  <optgroup label="Europe">
    <option value="GB" selected>United Kingdom</option>
    <option value="DE">Germany</option>
  </optgroup>
</select>

<!-- Multiple select -->
<select name="skills" multiple size="5">
  <option value="js">JavaScript</option>
  <option value="ts">TypeScript</option>
  <option value="react">React</option>
</select>

<!-- Textarea -->
<label for="bio">Bio</label>
<textarea
  id="bio"
  name="bio"
  rows="4"
  cols="50"
  minlength="20"
  maxlength="500"
  placeholder="Tell us about yourself..."
  autocomplete="off"
  spellcheck="true"
></textarea>  <!-- Note: no space between tags - whitespace is content -->

<!-- Datalist: free-text input with suggestions (not a constraint) -->
<label for="framework">Framework</label>
<input list="frameworks" id="framework" name="framework">
<datalist id="frameworks">
  <option value="React">
  <option value="Vue">
  <option value="Angular">
  <option value="Svelte">
  <option value="Next.js">
</datalist>

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free