HTML forms

The HTML label, explained.

A <label> is the visible name of a form field. Connected properly, it tells screen readers what the field is, and makes the text itself clickable. Here are the two ways to connect one, the common mistakes, and how to style labels.

Updated 7 November 2026

Connect a label with for and id

HTML
<label for="email">Email</label>
<input id="email" type="email" name="email" />
  • for on the label matches the id of the field. The id must be unique on the page.
  • Clicking the label focuses the field, or ticks it for a checkbox or radio button. That gives everyone a bigger target, especially on phones.
  • Screen readers announce the label when the field is focused: “Email, edit text”.
  • for points at id, not name. name is the key the value is sent under; id is what the label connects to. They are often the same word, but they do different jobs.

Or wrap the field in the label

Putting the field inside the label connects them without for and id. This is common for checkboxes and radio buttons, where the text sits right next to the box.

HTML
<label>
  Email
  <input type="email" name="email" />
</label>

Both ways work in current browsers and screen readers. Adding for and id even to a wrapped label is the most robust choice, because some older assistive software only understood the explicit link.

A placeholder is not a label

Placeholder text disappears as soon as someone starts typing, so they lose the field’s name exactly when they need it, and it is usually pale grey text that is hard to read. Use a visible label for the name, and keep placeholders for an example value, if anything.

For extra instructions, put them in their own element and connect it with aria-describedby. Screen readers read it after the label:

HTML
<label for="password">Password</label>
<input id="password" type="password" name="password" aria-describedby="password-hint" />
<p id="password-hint">At least 12 characters.</p>

A label you cannot see

Sometimes the design has no room for visible text, such as a search box with a button beside it. Keep a real label and hide it visually, so screen readers still announce it:

HTML
<form role="search" action="/search">
  <label for="q" class="visually-hidden">Search the site</label>
  <input id="q" type="search" name="q" />
  <button>Search</button>
</form>
CSS
/* Hidden on screen, still read by screen readers */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

aria-label="Search the site" on the input does the same job without a label element. A real label is better when you can use one: it is visible to translation tools and does not depend on ARIA support.

Rules worth knowing

  • One field per label. A label names one field. A field can have more than one label, but that is rarely useful.
  • Labels work on input (except hidden inputs), select, textarea, button, meter, output and progress.
  • Keep links and buttons out of labels where you can, because clicking the label then has two possible meanings. The common exception, an “I agree to the privacy policy” checkbox, works fine.
  • In React, the attribute is written htmlFor, because for is a reserved word in JavaScript.
JSX
<label htmlFor="email">Email</label>
<input id="email" type="email" name="email" />

Style labels

Labels are inline by default. Most forms put them on their own line above the field, which is the easiest layout to scan and works at every screen width:

CSS
label {
  display: block;
  margin: 16px 0 6px;
  font-weight: 600;
}
label .optional { font-weight: 400; color: #6b6a80; }

Say whether a field is optional or required in the label text itself. An asterisk or colour alone is easy to miss.

A labelled form that sends

This contact form posts to Heartdove, which saves every message, emails you an alert and tracks it until it is delivered or bounces. Labels are for people on your page and are not sent; each field arrives under a readable version of its name, so company shows up as “Company” in your inbox and the alert.

contact.html
<form action="https://submit.heartdove.com/v1/submit" method="POST">
  <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />

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

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

  <label for="company">Company <span class="optional">(optional)</span></label>
  <input id="company" type="text" name="company" autocomplete="organization" />

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

  <input type="checkbox" name="botcheck" tabindex="-1" autocomplete="off" style="display:none" />
  <button type="submit">Send message</button>
</form>

Replace YOUR_ACCESS_KEY with your key from a free Heartdove form, or let the contact form generator write the labels for you.

Questions

What does the for attribute do on a label?

It connects the label to the field whose id matches. Clicking the label then focuses the field, and screen readers announce the label for it.

Should I use for, or wrap the input in the label?

Either works. Using for and id is the most robust, and you can combine both: wrap the field and still add for and id.

Can I use a placeholder instead of a label?

No. A placeholder disappears when someone types and is often hard to read. Use a visible label, and a placeholder only for an example value.

What is the difference between a label and aria-label?

A label element is visible text connected to the field. aria-label gives a name only assistive technology can read. Prefer a visible label, or a visually hidden one, where you can.

Why is it htmlFor in React?

for is a reserved word in JavaScript, so React uses htmlFor. It produces the same for attribute in the HTML.

Never lose another enquiry.

Connect your first form in a few minutes. Your existing design stays exactly as it is.