MarzleyTech Learn

Home / Learn / HTML / Form input types and built-in validation

Form input types and built-in validation

In the Forms lesson you built a basic form. Now let's make forms that are easy on phones and catch mistakes before anything is sent, without writing any JavaScript.

Input types that help on phones

The type attribute changes both the checking and the keyboard a phone shows:

typeUse forPhone keyboard
textNames, general textLetters
emailEmail addressesHas @ and .
telPhone numbersNumber pad
numberQuantities, agesNumbers
urlWebsite addressesHas / and .com
passwordPasswordsHides what you type
dateDatesDate picker
searchSearch boxesHas a "search" key
checkboxYes/no, many choicesTick box
radioOne choice from a groupRound buttons
rangeA sliderSlider
fileUploadsFile or camera picker
colorColour pickerColour picker

Validation attributes

AttributeMeaningExample
requiredMust not be empty<input required>
minlength / maxlengthText length limitsminlength="8"
min / maxNumber or date limitsmin="1" max="10"
stepAllowed steps for numbersstep="50"
patternMust match a pattern (regular expression)pattern="0[17][0-9]{8}"
placeholderA hint inside the box (not a label!)placeholder="0712 345 678"
autocompleteHelps the browser fill it inautocomplete="email"

Try it: an order form that checks itself

Press Send with empty or wrong values and watch the browser stop you.

HTML · runs live in the interactive lesson
<style>
  body { font-family: sans-serif; }
  label { display: block; margin-top: 10px; font-weight: bold; }
  input, select { padding: 8px; width: 100%; max-width: 320px; box-sizing: border-box; }
  input:invalid:not(:placeholder-shown) { border: 2px solid #dc2626; }
  input:valid:not(:placeholder-shown) { border: 2px solid #16a34a; }
</style>
<form>
  <label for="name">Full name</label>
  <input id="name" name="name" required minlength="3" autocomplete="name" placeholder="Amina Wanjiru">

  <label for="phone">Safaricom number</label>
  <input id="phone" name="phone" type="tel" required pattern="0[17][0-9]{8}" placeholder="0712345678"
         title="10 digits starting with 07 or 01">

  <label for="email">Email</label>
  <input id="email" name="email" type="email" placeholder="you@example.com">

  <label for="qty">How many cakes? (1 to 10)</label>
  <input id="qty" name="qty" type="number" min="1" max="10" value="1" required>

  <label for="day">Delivery date</label>
  <input id="day" name="day" type="date" required>

  <label for="size">Size</label>
  <select id="size" name="size" required>
    <option value="">Choose a size</option>
    <option>1 kg</option>
    <option>2 kg</option>
  </select>

  <p><label><input type="checkbox" required> I agree to the terms</label></p>
  <button type="button" onclick="if (this.form.reportValidity()) document.getElementById('ok').textContent = 'Looks good! (practice form, nothing is sent)';">Send</button>
  <p id="ok"></p>
</form>

How the pattern works

pattern="0[17][0-9]{8}" reads as:

  • 0 : the first digit must be 0,
  • [17] : then a 1 or a 7,
  • [0-9]{8} : then exactly 8 more digits.

So 0712345678 and 0110123456 pass, but 712345678 and 07123 fail. Add a title to explain the format; browsers show it in the error message.

Radio buttons and checkboxes

Radio buttons in a group share the **same name**, so only one can be chosen. Wrap them in <fieldset> with a <legend> so everyone knows what the question is:

HTML · runs live in the interactive lesson
<form>
  <fieldset>
    <legend>How will you pay?</legend>
    <label><input type="radio" name="pay" value="mpesa" checked> M-Pesa</label>
    <label><input type="radio" name="pay" value="card"> Card</label>
    <label><input type="radio" name="pay" value="cash"> Cash on delivery</label>
  </fieldset>
  <fieldset>
    <legend>Extras</legend>
    <label><input type="checkbox" name="extra" value="candles"> Candles</label>
    <label><input type="checkbox" name="extra" value="card"> Greeting card</label>
  </fieldset>
</form>

Important: always check on the server too

Built-in validation helps honest users. Anyone can switch it off in their browser, so your server code (PHP, Python...) must check everything again before saving or charging money. You'll see this in the PHP lessons.

Good form habits

  • Every input needs a visible <label>. Placeholders disappear when you type.
  • Ask only for what you need. Every extra field loses customers.
  • Use the right type so phones show the right keyboard.
  • Put helpful formats in the label or title ("10 digits starting with 07").

Who depends on good forms

Every business website collects something: enquiries, bookings, orders, job applications, school admissions, newsletter sign-ups, M-Pesa payment numbers. The right input types and validation attributes make forms faster to fill on phones, reduce mistakes and cut down on fake or broken submissions, all before writing any JavaScript.

Every input type you should know

TypePhone keyboard / controlUse
textNormal keyboardNames, short answers
emailKeyboard with @ and .Email addresses (checks the format)
telNumber padPhone numbers
numberNumber pad with arrowsQuantities, ages (not phone numbers or IDs)
urlKeyboard with / and .comWebsite links
passwordHidden charactersPasswords
searchKeyboard with a "search" keySearch boxes
date, time, datetime-local, month, weekDate/time pickersBookings, deadlines
rangeSliderBudgets, ratings
colorColour pickerDesign tools
fileFile chooser / cameraUploads (CV, ID photo)
checkbox, radioTick boxes / choose oneOptions
hiddenNot shownExtra data sent with the form

Use type="tel" (not number) for phone numbers and ID numbers: number removes leading zeros, adds arrows and may show "e" for exponents.

Helpful attributes beyond validation

HTML
<input type="text" name="fullname" autocomplete="name" autocapitalize="words" required>
<input type="email" name="email" autocomplete="email" required>
<input type="tel" name="phone" autocomplete="tel" inputmode="tel" pattern="0[17][0-9]{8}" required>
<input type="text" name="otp" autocomplete="one-time-code" inputmode="numeric" maxlength="6">
<input type="password" name="new-password" autocomplete="new-password" minlength="8">
<input type="text" name="town" list="towns">
<datalist id="towns"><option value="Nairobi"><option value="Mombasa"><option value="Kisumu"><option value="Nakuru"><option value="Eldoret"></datalist>
  • autocomplete lets phones fill names, emails, phones and addresses in one tap and lets password managers work. one-time-code lets phones suggest SMS verification codes.
  • datalist gives suggestions while still allowing any value.
  • inputmode chooses the keyboard without changing validation.

Try it: a booking form

HTML · runs live in the interactive lesson
<style>
  form { font: 14px system-ui; max-width: 340px; }
  label { display: block; margin-top: 10px; font-weight: 600; }
  input, select, textarea { width: 100%; box-sizing: border-box; padding: 8px; border: 2px solid #cbd5e1; border-radius: 8px; font: inherit; margin-top: 4px; }
  input:user-invalid, select:user-invalid { border-color: #dc2626; }
  fieldset { border: 1px solid #e2e8f0; border-radius: 8px; margin-top: 12px; }
  fieldset label { display: inline; font-weight: 400; }
  button { margin-top: 14px; padding: 10px 16px; background: #0b1b35; color: #fff; border: 0; border-radius: 8px; font-weight: 700; }
  output { display: block; margin-top: 8px; }
</style>
<form id="book">
  <label for="n">Full name</label>
  <input id="n" name="name" autocomplete="name" required minlength="3">
  <label for="p">Phone</label>
  <input id="p" name="phone" type="tel" autocomplete="tel" required pattern="0[17][0-9]{8}" title="10 digits starting with 07 or 01" placeholder="0712345678">
  <label for="s">Service</label>
  <select id="s" name="service" required>
    <option value="">Choose a service</option>
    <option>Website design</option><option>Computer repair</option><option>Training</option>
  </select>
  <label for="d">Preferred date</label>
  <input id="d" name="date" type="date" required>
  <label for="b">Budget: <output id="bo">20000</output></label>
  <input id="b" name="budget" type="range" min="5000" max="100000" step="5000" value="20000">
  <fieldset>
    <legend>Contact me by</legend>
    <input type="radio" id="c1" name="contact" value="call" required><label for="c1">Call</label>
    <input type="radio" id="c2" name="contact" value="whatsapp"><label for="c2">WhatsApp</label>
  </fieldset>
  <label for="m">Details (optional)</label>
  <textarea id="m" name="details" rows="3" maxlength="300"></textarea>
  <label><input type="checkbox" name="consent" required style="width:auto"> I agree to be contacted about this booking</label>
  <button>Book now</button>
  <output id="res"></output>
</form>
<script>
  const d = document.getElementById("d");
  d.min = new Date().toISOString().slice(0, 10);          // no past dates
  const b = document.getElementById("b");
  b.addEventListener("input", () => document.getElementById("bo").textContent = Number(b.value).toLocaleString());
  document.getElementById("book").addEventListener("submit", e => {
    e.preventDefault();
    document.getElementById("res").textContent = "Booked: " + JSON.stringify(Object.fromEntries(new FormData(e.target)));
  });
</script>

Try submitting with missing fields, a wrong phone number or a past date (the date picker won't allow it).

Custom messages with setCustomValidity

JavaScript
const phone = document.querySelector("#p");
phone.addEventListener("input", () => {
  phone.setCustomValidity("");
  if (phone.validity.patternMismatch) {
    phone.setCustomValidity("Enter a Kenyan mobile number like 0712345678");
  }
});

The browser's default messages can be vague ("Please match the requested format"). Custom messages tell users exactly what to do.

Accessible forms checklist

  • Every input has a visible <label> connected with for/id (placeholders are not labels).
  • Group radio buttons and checkboxes in <fieldset> with a <legend>.
  • Mark required fields clearly (text such as "required", not only a red star).
  • Put help text near the field and connect it with aria-describedby.
  • Error messages say what went wrong and how to fix it.
  • The form works with the keyboard alone (Tab, Space, Enter).

Where form data goes

HTML
<form action="/contact.php" method="post">           <!-- your own server script -->
<form action="https://formspree.io/f/xxxx" method="post">  <!-- a form service -->
<form action="/search" method="get">                  <!-- data appears in the URL: good for search -->
  • Use method="post" for personal data, messages and anything that changes data.
  • Use get for searches and filters, so results can be bookmarked and shared.
  • Add enctype="multipart/form-data" when uploading files.
  • Protect forms from spam with a hidden honeypot field, rate limiting or a CAPTCHA, and always validate on the server.

Common mistakes

MistakeFix
type="number" for phone numberstype="tel"
Placeholder instead of a labelVisible <label>
Asking for unnecessary dataAsk only what you need (Data Protection Act)
Long single-page forms with no groupingUse fieldsets or steps
No message after submittingShow a clear success message or thank-you page
Sending personal data with getpost

Practice

  1. Build a school admission form with student details, parent phone, class (select) and a file upload for a birth certificate.
  2. Add autocomplete values to every field of a checkout form.
  3. Create a job application form that only accepts PDF uploads (accept=".pdf").
  4. Write custom error messages for an email and a phone field.
  5. Make a search form that uses get and check how the URL changes.
Think about it: Why is type="tel" better than type="number" for a phone field like 0712345678?Show answer

number treats the value as a quantity: it can drop the leading zero, show increment arrows, accept "e" for exponents and reject spaces or "+254". tel keeps the value as text exactly as typed while still showing a number keypad on phones; combine it with pattern for validation.

Check yourself

  1. Which input type shows a number pad for phone numbers on mobile?

    Show answer

    tel

  2. Which attribute makes a field compulsory?

    Show answer

    required

  3. Radio buttons in the same group must share the same value for which attribute?

    Show answer

    name

  4. Which element gives a group of inputs a caption, inside a fieldset?

    Show answer

    legend

  5. Is browser validation enough on its own, or must the server check again too?

    Show answer

    server

  6. Which attribute value lets phones auto-fill SMS verification codes?

    Show answer

    one-time-code

  7. Which element provides suggestions for a text input while allowing any value?

    Show answer

    datalist

  8. Which form method should you use for search forms so results can be bookmarked?

    Show answer

    get

  9. Which enctype is required for file uploads?

    Show answer

    multipart/form-data

Exercise

Make an input for a Kenyan phone number: type="tel", required and the pattern 0[17][0-9]{8}, with a label.

Do this exercise in the live editor

Lesson 14 of 19 in HTML · Printable course notes