Forms and Tables

Table of Contents

  1. What a Form Does
  2. Labels and Inputs
  3. Input Types
  4. Textarea, Select, Checkboxes, and Radios
  5. Buttons
  6. Built-in Validation
  7. Where the Data Goes
  8. Tables
  9. Try It Yourself

1. What a Form Does

A form collects input from the user and sends it somewhere. Login boxes, search bars, checkout pages, and comment fields are all forms. HTML gives you every control you need — text boxes, checkboxes, menus, buttons — and the browser provides the behavior: keyboard support, mobile keyboards, and basic validation. All before you write any JavaScript.

The wrapper is the <form> element:

<form action="/contact" method="post">
  <!-- controls go here -->
</form>

action is the URL the data is sent to and method is the HTTP method from HTTP Requests. Section 7 explains what to do on a static site, where there is no server to send to.

2. Labels and Inputs

The core pair is a <label> and an <input>:

<label for="name">Your name</label>
<input type="text" id="name" name="name">

Three attributes link everything together:

  • for on the label matches the id of the input. This connects them: clicking the label focuses the field, and screen readers announce the label when the field is focused.
  • name is the key under which the value is sent. Without it, the field's value is not sent at all.
  • id must be unique on the page.

Every form field needs a label. A placeholder is not a label — it disappears as soon as you type, and it's often too faint to read. You can use a placeholder as a hint (placeholder="e.g. Dara Sok") in addition to a label, never instead.

You can also wrap the input inside the label, in which case for and id aren't needed:

<label>Your name <input type="text" name="name"></label>

3. Input Types

The type attribute turns one element into many controls:

TypeGives youNotes
textA single-line text boxThe default
emailText box that checks for an @Mobile keyboards show @
telPhone numberMobile shows a number pad
urlA web addressMust start with a scheme
numberNumeric box with steppersSupports min, max, step
passwordDots instead of lettersHides the text, but is not encrypted by itself
dateDate pickerValue format is YYYY-MM-DD
searchA search boxOften has a clear button
fileFile chooserNeeds enctype on the form
rangeA slider
colorColor picker
hiddenNothing visibleSends a fixed value

Picking the right type is a free upgrade: the right keyboard on phones, automatic validation, and sometimes a native date or color picker.

4. Textarea, Select, Checkboxes, and Radios

<textarea> is for multi-line text. It has a closing tag, and rows sets its height:

<label for="message">Message</label>
<textarea id="message" name="message" rows="5"></textarea>

<select> is a drop-down menu of <option>s:

<label for="topic">Topic</label>
<select id="topic" name="topic">
  <option value="">Choose one…</option>
  <option value="job">Job opportunity</option>
  <option value="project">Project collaboration</option>
  <option value="other">Something else</option>
</select>

Checkboxes are for zero or more choices; radio buttons are for exactly one. Radios in the same group share the same name. Wrap a group in a <fieldset> with a <legend> that names it:

<fieldset>
  <legend>How should I reply?</legend>
  <label><input type="radio" name="reply" value="email" checked> Email</label>
  <label><input type="radio" name="reply" value="phone"> Phone</label>
</fieldset>

<label>
  <input type="checkbox" name="newsletter" value="yes">
  Send me occasional updates
</label>

5. Buttons

The <button> element is a clickable control, and its type matters:

<button type="submit">Send</button>
<button type="reset">Clear</button>
<button type="button">Does nothing by itself</button>
  • submit sends the form (the default inside a form).
  • reset puts every field back to its starting value.
  • button does nothing until JavaScript is attached to it.

Use <button> for actions and <a> for navigation — a link goes somewhere, a button does something. Don't make a <div> look like a button; you'd lose keyboard support and screen-reader meaning.

6. Built-in Validation

Browsers can check input before the form is submitted. Add attributes and the browser stops submission and shows a message when something's wrong:

AttributeEffect
requiredCan't be empty
minlength, maxlengthLimits on text length
min, maxLimits on numbers and dates
pattern="[0-9]{9,10}"Must match a regular expression
type="email"Must look like an email address
<label for="email">Email</label>
<input type="email" id="email" name="email" required>

Try submitting with it empty, then with abc. The browser blocks the submit and tells you why — no JavaScript involved.

One warning: browser validation is a convenience, not security. Anyone can bypass it by editing the page in DevTools or sending a request directly with curl. Any real server must check the data again.

7. Where the Data Goes

When you press Submit, the browser builds an HTTP request out of every field that has a name. With method="get" the values go into the query string — ?name=Dara&topic=job, as seen in the URL chapter. With method="post" they go in the request body. Either way, something on a server must receive them.

A static site has no server of its own, so it can't process a form by itself. You have three honest options:

  1. mailto: — a plain link that opens the visitor's email app. Simple, and works anywhere, but not a real form.
  2. A form service such as Formspree or Web3Forms. They give you a URL to use as action, receive the data, and email it to you.
  3. Your own backend — beyond this series.

For this project, build the form so the HTML is complete and correct, and point action at a form service later if you want it live. The markup you write is the same either way.

<form action="https://example.com/your-form-url" method="post">
  <h2>Get in touch</h2>

  <label for="name">Name</label>
  <input type="text" id="name" name="name" required>

  <label for="email">Email</label>
  <input type="email" id="email" name="email" required>

  <label for="message">Message</label>
  <textarea id="message" name="message" rows="5" required></textarea>

  <button type="submit">Send</button>
</form>

8. Tables

A <table> is for tabular data — information that has rows and columns, like a schedule or a price list. Not for page layout (that's what chapters 9 and 10 are for).

<table>
  <caption>My skills</caption>
  <thead>
    <tr>
      <th scope="col">Skill</th>
      <th scope="col">Level</th>
      <th scope="col">Since</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>HTML</td>
      <td>Comfortable</td>
      <td>2026</td>
    </tr>
    <tr>
      <td>CSS</td>
      <td>Learning</td>
      <td>2026</td>
    </tr>
  </tbody>
</table>

The parts:

ElementRole
<caption>Title of the table
<thead>, <tbody>, <tfoot>Header, body, and footer row groups
<tr>A table row
<th>A header cell. scope="col" or scope="row" says which direction it labels
<td>A data cell

Header cells with scope let a screen reader announce "Level: Comfortable" for a cell, instead of reading a bare "Comfortable" with no context. To merge cells use colspan and rowspan.

A table with no borders looks like plain text. That's expected — it's CSS's job to make it look good, and you'll do that in chapter 6.

9. Try It Yourself

  1. Add the contact form. Put the form from section 7 inside the #contact section of index.html. Check that clicking each label focuses its field.
  2. Test validation. Submit it empty, then with an invalid email. Add a minlength="10" on the message and test again.
  3. Try input types. Add <input type="date">, type="range", and type="color" to a scratch file. Open the page on your phone via Live Server's network address, or in DevTools device mode, and compare the keyboards for text, email, and tel.
  4. Bypass your own validation. In DevTools, delete the required attribute from a field and submit it empty. This is why a real server must validate too.
  5. Add a skills table to the About page with a <caption>, header cells with scope, and at least four rows.
  6. Inspect the traffic. Change the form to method="get" and action="https://httpbin.org/get", submit, and read the query string in the address bar.

Commit your work. HTML is now done: you can structure any page. From the next chapter on, we make it look good with CSS.

Buy Me a Coffee

Found this tutorial helpful? Consider buying me a coffee to support more free content!