FullStack Course LogoFullStack Course
Module: HTML
HTML·008·10 MIN READ

008: Forms I

TOPICS COVERED: Forms I

Learning outcomes

By the end of this lesson, you can build a basic registration form using form, explicitly associated labels, inputs, and buttons. You will also be able to explain what action, method, name, and value do, select appropriate text, email, and password input types, and trace the successful controls that become submitted name/value pairs.

Prerequisites and retrieval

Start with the semantic portfolio. Recall why link text needs an accessible name and why a button performs an action instead of navigation. Also trace the HTTP request from lesson 001: a submitted form is another request sent to a server endpoint.

Terminology

  • Form: “The form element represents a collection of form-associated elements, some of which can represent editable values.” — Source: WHATWG: The form element
  • Control: An interactive form-associated element (input, button, select, textarea) able to contribute submission entries. — Source: WHATWG: Form control infrastructure
  • Label: “The label element represents a caption in a user interface for the element’s value.” — Source: WHATWG: The label element
  • Endpoint: The submission URL supplied by the form’s action attribute. — Source: WHATWG: Form submission
  • Method: The HTTP verb (get or post) selecting how form entries are submitted. — Source: WHATWG: method attribute
  • Name/value pair: The key and data each successful control contributes to the submitted entry list. — Source: WHATWG: Constructing the entry list
  • Successful control: A control eligible to contribute entries when the form is submitted (not disabled, checked, named…). — Source: WHATWG: Form control infrastructure
  • Accessible name: The programmatic label exposed to assistive technology, usually from an associated label. — Source: W3C WAI: Labeling controls
  • Autocomplete token: A standardized token (email, name, new-password…) hinting the expected kind of user data. — Source: WHATWG: Autofill
  • Input (input): "The input element represents a typed data field, allowing the user to edit the data." — Source: WHATWG: The input element
  • Form owner: "The form element that a form-associated element is associated with." — Source: WHATWG: Form owner
  • Enctype: "The enctype attribute specifies how form data is encoded for submission (e.g., application/x-www-form-urlencoded)." — Source: WHATWG: Form submission

Mental model: labeled fields become an envelope

A form is more than a collection of boxes on the screen. Each control needs a human-facing instruction (label), a programmatic key for submission (name), and a current value. When the user submits, the browser builds entries like these:

text
full-name=Asha Rao
email=asha@example.com
password=(entered value)

id connects a label to one element and identifies that element uniquely in the document. name identifies the data sent to the server. The two attributes often have the same text because that is convenient, but they solve different problems. A control with no name can still work perfectly well on screen while contributing no named form data.

form, action, and method

html
<form action="/register" method="post">
  <!-- controls -->
</form>

action resolves to the URL where the submission goes. With method="get", the browser encodes the form data in the URL query. That fits safe retrieval and search operations. With method="post", it sends the data in the request body, which fits operations that change server state or data that does not belong in a URL. POST is not encryption, so use HTTPS. The server still has to process, validate, authorize, and store the data safely.

When you are working with static course files and no server, use a clearly fictional endpoint such as /register and expect the submission not to complete. Never send personal test data to a real third-party endpoint.

Labels and inputs

html
<label for="full-name">Full name</label>
<input type="text" id="full-name" name="full-name" autocomplete="name">

The for value must exactly match the unique id of the associated control. That association means clicking the label focuses or activates the field, making the target easier to use and making the relationship clear. A placeholder is not a label: it disappears as the user types, can have poor contrast, and generally does not provide persistent instructions.

type="text" is the general-purpose single-line text field. type="email" provides email-oriented input behavior and, later, native format checking. type="password" visually obscures the characters, but it does not encrypt them or make storage safe. Use autocomplete values that describe the data accurately:

html
<input type="email" autocomplete="email">
<input type="password" autocomplete="new-password">

Autocomplete can make forms faster, reduce typing errors, and help people with cognitive or motor disabilities. Do not turn it off reflexively. Authentication forms have distinct tokens for current passwords and new passwords, so choose the token that matches the field.

Buttons, names, and values

html
<button type="submit">Create account</button>

A button inside a form defaults to submitting that form. Writing type="submit" makes the intent explicit. type="button" has no behavior without script, while type="reset" resets the controls and is covered tomorrow. The button's visible content tells the user what action it performs.

For a text input, the user supplies the value. A value attribute supplies an initial value; do not use it to prefill personal information or passwords. On buttons and choice controls, value can specify the machine-readable value sent to the server. A placeholder is neither an initial value nor a submitted value.

The submission contract: method, encoding, and control state

A form defines a contract between the document and the endpoint that receives its data. Three details account for many real bugs:

  1. Only successful controls are submitted. A control generally needs a name before it can contribute data.
  2. Disabled controls are not submitted. If a value must be sent but should not be editable, readonly may be suitable for supported text-like controls. Do not treat disabled as “read-only and still submitted.”
  3. Encoding must match the data. Ordinary forms commonly use the default application/x-www-form-urlencoded; file uploads require multipart/form-data, as lesson 017 explains.

Buttons are worth making explicit in reusable components too:

html
<button type="submit">Create account</button>
<button type="button">Show password rules</button>

Because a plain <button> inside a form defaults to submit, type="button" prevents a control intended for later client-side behavior from unexpectedly sending a request.

Use autocomplete tokens whenever they accurately describe the field:

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

Good autocomplete metadata reduces typing and errors, particularly on mobile devices and for people with cognitive or motor impairments.

Guided example: basic registration form

Create register.html with the same shared site landmarks:

html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Register | Asha Rao</title>
  </head>
  <body>
    <header>
      <p>Asha Rao</p>
      <nav aria-label="Primary">
        <ul>
          <li><a href="index.html">Home</a></li>
          <li><a href="about.html">About</a></li>
          <li><a href="register.html" aria-current="page">Register</a></li>
        </ul>
      </nav>
    </header>
    <main>
      <h1>Register for project updates</h1>
      <p>Your password will be handled by a server in a later module.</p>
      <form action="/register" method="post">
        <p>
          <label for="full-name">Full name</label>
          <input type="text" id="full-name" name="full-name" autocomplete="name">
        </p>
        <p>
          <label for="email">Email address</label>
          <input type="email" id="email" name="email" autocomplete="email">
        </p>
        <p>
          <label for="password">Create password</label>
          <input type="password" id="password" name="password" autocomplete="new-password">
        </p>
        <button type="submit">Create account</button>
      </form>
    </main>
    <footer><p><a href="contact.html">Contact Asha</a></p></footer>
  </body>
</html>

Test the label clicks. Press Tab and predict the focus order from the source order. Enter fictional values. In the network tools, you may be able to see the intended request even when the endpoint fails. Never enter a real password in a demo. For each entry, be able to explain the chain: the email label identifies the field, id creates the association, name="email" supplies the submission key, and the typed address supplies its value.

Using paragraphs to group a label/control pair is acceptable for this simple structure, although a div would also be neutral. A paragraph cannot contain arbitrary later block structures, so keep each grouping straightforward.

Intermediate example: distinguish GET and POST

Build a safe site search:

html
<form action="/search" method="get">
  <label for="query">Search projects</label>
  <input type="search" id="query" name="q">
  <button type="submit">Search</button>
</form>

Searching for “weather app” conceptually navigates to /search?q=weather+app. That URL can be bookmarked and shared, which makes it suitable for non-sensitive search state. Registration uses POST because it changes server state and includes credentials. Even so, POST data is visible to the receiving server and to relevant tools, and HTTPS is still required in transit.

Temporarily remove name="q" and submit the search. The query disappears, which demonstrates why name matters. Restore it. Then temporarily change for="query" to a nonmatching value; clicking the label no longer focuses the field. Restore that too. When debugging forms, change one relationship at a time so each observed result has a clear cause.

Advanced optional extension: submission details

Only successful controls contribute entries. Disabled controls are omitted. An unchecked checkbox contributes nothing. A button contributes its own name/value only when that button is used to submit. Duplicate names can intentionally produce multiple values, as they do in a checkbox group, but the server must be prepared to receive them.

HTML form data is not a JavaScript object, and it does not automatically become JSON. The default encoding for ordinary POST forms is application/x-www-form-urlencoded; file uploads need multipart/form-data, which is covered later. Keeping that distinction clear prevents the mistaken idea that HTML has selected a database schema for you.

Predict the entry list rather than counting the controls visible on screen. A submit button contributes only when it caused the submission, and pressing Enter may submit through the form's default submitter. Give every button an explicit type; otherwise a button inside a form defaults to submit and can generate an accidental request. In a form with name="email" and an unchecked name="updates" checkbox, the checkbox key is absent, not updates=false. The server has to define what that missing value means.

Common mistakes and debugging

  • Label with no matching control: verify the exact for/id pair and make sure IDs are unique.
  • Placeholder as only label: add a persistent visible label.
  • Missing name: inspect the submission entry list.
  • Duplicate IDs: run the conformance checker.
  • Password type treated as security: use HTTPS and secure server handling; masking provides only visual privacy.
  • GET used for secrets: query data appears in URLs, browser history, logs, and referrals.
  • Button with unspecified intent: state type, especially in reusable components.
  • Real data sent to a demo endpoint: use fictional values and a controlled server.
  • Autocomplete disabled: provide accurate tokens unless there is a concrete reason not to.

Accessibility, security, and performance

Every control needs a programmatically associated label or an equivalent accessible name; a visible label is the robust default. Label text should describe the data the user is expected to enter. Keep source order aligned with reading and focus order. Native controls already provide keyboard, touch, zoom, and assistive-technology behavior that generic scripted boxes do not.

Client-side markup cannot secure a form. The eventual application needs HTTPS, server-side validation, output encoding, authorization, CSRF defenses where relevant, rate limits, and safe password hashing. Collect only the data the application needs and explain how it will be used. Forms themselves are lightweight, but unnecessary third-party scripts can delay interaction or collect input. Accurate autocomplete reduces user effort and errors.

Tiered exercises

Level 1: associate

Create full-name and email controls. Give each a unique id, a matching label, a useful name, the correct type, and an appropriate autocomplete token.

Level 2: register

Build the complete registration form with text, email, password, a POST action, and an explicit submit button. Trace the three resulting name/value pairs.

Level 3: compare

Add a GET search form. Submit fictional values, inspect both request destinations, remove and restore one name, and test the labels and focus order with the keyboard.

Level 1:

html
<label for="full-name">Full name</label>
<input type="text" id="full-name" name="full-name" autocomplete="name">
<label for="email">Email address</label>
<input type="email" id="email" name="email" autocomplete="email">

Level 2: use the guided form. With fictional input Sam Lee, sam@example.com, and a demo password, the keys are full-name, email, and password. POST places encoded entries in the body rather than the URL, but only HTTPS protects transit, and the server remains responsible for security.

Level 3: use the intermediate search. Its visible destination contains ?q=...; registration targets /register with POST. Without name="q", no q entry exists. Clicking each label focuses its control, and Tab follows source order through the controls and submit button.

Recap and exit questions

Forms gather labeled values and submit named entries to endpoints. IDs associate labels, names identify submitted data, input types provide native behavior, and methods express the intent of the request.

  1. Why are id and name not interchangeable?
  2. When is GET suitable?
  3. Why is a placeholder not a label?
  4. Does type="password" encrypt a value?
  5. What happens to a control without name?
  6. Why can an apparently harmless button submit a form?

Try it with your own example

Build one more form yourself before the next lesson introduces more control types. Keep it short; that makes a for/id mismatch easy to spot and fix without someone else supplying the answer.

Rina wants a simple newsletter signup at the bottom of her homepage: just a name and an email, POSTing to a fictional endpoint. Write it from the pattern above without copying it line for line:

html
<h2>Get weekly bake announcements</h2>
<form action="/subscribe" method="post">
  <p>
    <label for="subscriber-name">First name</label>
    <input type="text" id="subscriber-name" name="first-name" autocomplete="given-name">
  </p>
  <p>
    <label for="subscriber-email">Email address</label>
    <input type="email" id="subscriber-email" name="email" autocomplete="email">
  </p>
  <button type="submit">Subscribe</button>
</form>

Now break it deliberately, as you did with links in lesson 004: change for="subscriber-email" to for="subscriber-mail" (one letter off) and click the label. Nothing happens because the field does not focus. That quiet failure is what a real typo looks like in production; recognizing the symptom lets you inspect the for/id pair immediately instead of wondering why the form feels broken.

Further reading: MDN — Sending form data shows what the actual HTTP request body looks like for a form exactly like this one.

Official references

Reader page: /html/lesson/008/forms-i