Skip to main content
Accessibility

How to Fix 'Form Elements Must Have Labels' Accessibility Error

Why form inputs need associated labels, how screen readers use them, and how to fix unlabeled inputs with label elements or aria attributes.

Updated

How to Fix “Form Elements Must Have Labels” Accessibility Error

Run an accessibility audit with Lighthouse or axe DevTools and you’ll see this:

Form elements do not have associated labels

This means you have <input>, <select>, or <textarea> elements that aren’t programmatically connected to a label. Sighted users might see a label visually, but screen readers and other assistive technologies cannot determine what the input is for.

Why labels matter

When a screen reader encounters a form input, it announces the input’s accessible name. That name comes from an associated <label>, an aria-label, or an aria-labelledby attribute. Without any of these, the screen reader announces something like “edit text, blank” or just “text input.” The user has no idea what to type.

Labels also improve usability for all users. A properly associated <label> makes the label text clickable, expanding the click target to include the label. This helps on mobile and benefits users with motor impairments.

Common causes

1. Input with no label element at all

The most straightforward case. There’s an input, maybe with a visual label nearby in a <span> or <div>, but no <label> element connected to it.

<!-- Bad — the span is not connected to the input -->
<span>Email</span>
<input type="email" id="email">

Fix (explicit label): Use a <label> with a for attribute matching the input’s id.

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

Fix (implicit label): Wrap the input inside the <label>. No for or id needed.

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

Both approaches are valid. Explicit labels are more flexible for layout since the label and input don’t need to be adjacent in the DOM.

2. Using placeholder as a substitute for a label

Placeholder text disappears as soon as the user starts typing. It is not a label. Screen readers may or may not read placeholder text depending on the browser and assistive technology combination. Even when they do, the text vanishes visually, leaving users with short-term memory challenges unable to verify what field they’re filling in.

<!-- Bad — placeholder is not a label -->
<input type="text" placeholder="Enter your name">

Fix: Add a real label. If you want to keep the visual design clean, hide the label visually while keeping it accessible.

<label for="name" class="sr-only">Full name</label>
<input type="text" id="name" placeholder="Enter your name">

The sr-only class (screen-reader only) hides the label visually but keeps it in the accessibility tree:

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

3. Icon-only inputs and buttons

Search bars and icon buttons often skip visible labels for visual minimalism. The icon makes sense visually, but assistive technology has no idea what the input does.

<!-- Bad -->
<input type="search">
<button><svg>...</svg></button>

Fix: Use aria-label to provide an accessible name without a visible label.

<input type="search" aria-label="Search articles">
<button aria-label="Submit search"><svg aria-hidden="true">...</svg></button>

Note that aria-label overrides any other labeling mechanism. If you have both a <label> and an aria-label, the aria-label wins. Use one or the other, not both.

4. Mismatched for and id attributes

The label exists and uses a for attribute, but the value doesn’t match any input’s id. A typo or a refactor that changed the id without updating the label.

<!-- Bad — for="user-email" but id="email" -->
<label for="user-email">Email</label>
<input type="email" id="email">

Fix: Make the for attribute match the input’s id exactly.

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

5. Dynamic forms generated by JavaScript

Client-side form builders and frameworks sometimes inject inputs without labels. The HTML that reaches the DOM has inputs but no label associations, even if the source code looks fine.

Fix: Inspect the rendered DOM, not the source code. Open DevTools, find the input, and check whether it has an associated label. The Accessibility tab in Chrome DevTools shows the computed accessible name for any element. If it’s empty, the input has no label.

Using aria-labelledby for complex layouts

When the label text comes from a heading or another element that isn’t a <label>, use aria-labelledby to point at that element’s id.

<h3 id="shipping-heading">Shipping Address</h3>
<input type="text" aria-labelledby="shipping-heading field-street" id="street">
<span id="field-street">Street</span>

aria-labelledby accepts multiple IDs separated by spaces. The screen reader concatenates the text content of all referenced elements. In this example, it announces “Shipping Address Street.”

Testing for label issues

Several tools catch this automatically:

  • Lighthouse accessibility audit in Chrome DevTools
  • axe DevTools browser extension (more detailed results than Lighthouse)
  • eslint-plugin-jsx-a11y catches missing labels at lint time in React projects
  • Tab through the form with a screen reader (VoiceOver on macOS, NVDA on Windows) to hear exactly what gets announced

A good rule of thumb: if you tab to an input and can’t tell what it’s for without looking at the screen, it needs a better label.

Prevention

Every form input needs one of these, in order of preference:

  1. A <label> element (explicit with for/id, or implicit by wrapping)
  2. An aria-label attribute (for icon-only or visually hidden labels)
  3. An aria-labelledby attribute (for labels from existing page elements)

placeholder and title are not acceptable substitutes.

Hushbug detects unlabeled form elements and other accessibility violations automatically while you browse. Coming soon to the Chrome Web Store.