Fix Form Labels for Better Accessibility

Learn how to fix missing form labels in Lighthouse accessibility audits. Labels ensure form controls are announced properly by assistive technologies.
Harlan WiltonHarlan Wilton4 min read Published

Unlabeled form fields are invisible to screen reader users. Without a label, users cannot understand what information to enter.

What's Happening

Form elements like <input>, <select>, and <textarea> need associated labels to be accessible. When a label is missing, screen readers announce the field as just "edit text" or "combo box" without context. Users cannot determine whether they should enter their name, email, or credit card number.

Diagnose

Chrome DevTools

  1. Open DevTools (F12)
  2. Go to Elements panel
  3. Find form elements and check for:
    • Missing <label> element nearby
    • Missing for attribute matching input id
    • Missing aria-label or aria-labelledby attribute

Quick console check:

document.querySelectorAll('input, select, textarea').forEach((el) => {
  const hasLabel = el.labels?.length > 0
  const hasAriaLabel = el.hasAttribute('aria-label')
  const hasAriaLabelledby = el.hasAttribute('aria-labelledby')

  if (!hasLabel && !hasAriaLabel && !hasAriaLabelledby) {
    console.warn('Unlabeled form element:', el)
  }
})

Accessibility Inspector

  1. Open DevTools > Accessibility tab
  2. Select the form element
  3. Check "Computed Properties" - the Name should not be empty

Fix

1. Use <label> with for Attribute

The explicit label association using matching for and id attributes:

<!-- Before: No label -->
<input type="email" id="email">

<!-- After: Properly labeled -->
<label for="email">Email address</label>
<input type="email" id="email">

This is the most robust method. Clicking the label focuses the input, improving usability for everyone.

2. Wrap Input in <label>

Implicit association by wrapping the input inside the label:

<!-- Implicit label - works without for/id -->
<label>
  Email address
  <input type="email">
</label>

This approach is simpler but less flexible for styling. Both methods are equally accessible.

3. Use aria-label for Visual Labels

When a visible label exists but isn't associated, or when the design requires no visible label:

<!-- Icon-only search button -->
<button aria-label="Search">
  <svg>...</svg>
</button>

<!-- Input with visible placeholder but no label -->
<input type="search" aria-label="Search products" placeholder="Search...">

Use aria-label sparingly - visible labels are better for all users, not just screen reader users.

4. Use aria-labelledby for Complex Cases

When the label text exists elsewhere on the page:

<h2 id="contact-heading">Contact Information</h2>
<div id="phone-hint">Include country code</div>

<input
  type="tel"
  aria-labelledby="contact-heading phone-hint"
>

Multiple IDs can be space-separated to combine text from several elements.

Framework Examples

Nuxt / VueWith Nuxt UI, form components handle labels automatically:
<template>
  <UFormField label="Email address" name="email">
    <UInput v-model="email" type="email" />
  </UFormField>
</template>
For custom inputs, always bind labels:
<script setup>
const inputId = useId()
</script>

<template>
  <div>
    <label :for="inputId">{{ label }}</label>
    <input :id="inputId" v-model="value">
  </div>
</template>
ReactUse htmlFor instead of for:
function EmailField() {
  const id = useId()
  return (
    <div>
      <label htmlFor={id}>Email address</label>
      <input id={id} type="email" />
    </div>
  )
}

Verify the Fix

  1. Re-run Lighthouse - The "Form elements have associated labels" audit should pass
  2. Test with screen reader - Tab to each form field and verify the label is announced
  3. Click test - Clicking a label should focus its associated input

Console verification:

// Should return empty array
[...document.querySelectorAll('input, select, textarea')].filter((el) => {
  const hasLabel = el.labels?.length > 0
  const hasAria = el.hasAttribute('aria-label') || el.hasAttribute('aria-labelledby')
  return !hasLabel && !hasAria
})

Common Mistakes

  • Placeholder as label — Placeholders disappear when users type. They are hints, not labels. Always provide a real label.
  • Label not associated — A <label> element near the input is not enough. Use for attribute or wrap the input inside the label.
  • Empty aria-labelaria-label="" is worse than no label at all. Always provide meaningful text.
  • Labeling hidden inputs — Inputs with type="hidden" should not have labels. Screen readers ignore hidden inputs.
  • Duplicate IDs — If two inputs share the same id, only one gets the label. IDs must be unique per page.

Form label issues often appear alongside:

  • Button Name — Submit buttons and form controls both need names
  • Link Name — Form action links need descriptive text
  • Color Contrast — Low-contrast labels are hard to read

Test Your Entire Site

Form labels are easy to miss across a large site, especially in footers, search bars, and newsletter signups that appear on every page. Unlighthouse scans your entire site and identifies every page with unlabeled form elements, helping you fix them systematically.