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.

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

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

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 strongest method. Clicking the label focuses the input, improving usability for everyone.

2. Wrap input in

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...">

Limit aria-label use; visible labels are better for all 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-label: aria-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:

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. Greenland

\n\n\n",[4294,5193,5194,5206,5222,5230,5234,5242,5250,5279,5309,5317],{"__ignoreMap":1843},[4299,5195,5196,5198,5201,5204],{"class":4404,"line":1766},[4299,5197,4302],{"class":4301},[4299,5199,5200],{"class":4305},"script",[4299,5202,5203],{"class":4679}," setup",[4299,5205,4705],{"class":4301},[4299,5207,5208,5211,5214,5216,5219],{"class":4404,"line":1756},[4299,5209,5210],{"class":4448},"const",[4299,5212,5213],{"class":4460}," inputId",[4299,5215,4465],{"class":4464},[4299,5217,5218],{"class":4413}," useId",[4299,5220,5221],{"class":4407},"()\n",[4299,5223,5224,5226,5228],{"class":4404,"line":1828},[4299,5225,4739],{"class":4301},[4299,5227,5200],{"class":4305},[4299,5229,4705],{"class":4301},[4299,5231,5232],{"class":4404,"line":1749},[4299,5233,4545],{"emptyLinePlaceholder":4544},[4299,5235,5236,5238,5240],{"class":4404,"line":1984},[4299,5237,4302],{"class":4301},[4299,5239,5102],{"class":4305},[4299,5241,4705],{"class":4301},[4299,5243,5244,5246,5248],{"class":4404,"line":4548},[4299,5245,4811],{"class":4301},[4299,5247,5004],{"class":4305},[4299,5249,4705],{"class":4301},[4299,5251,5252,5254,5256,5259,5261,5263,5266,5268,5270,5273,5275,5277],{"class":4404,"line":4586},[4299,5253,5141],{"class":4301},[4299,5255,4367],{"class":4305},[4299,5257,5258],{"class":4679}," :for",[4299,5260,4683],{"class":4301},[4299,5262,4686],{"class":4420},[4299,5264,5265],{"class":4424},"inputId",[4299,5267,4686],{"class":4420},[4299,5269,4309],{"class":4301},[4299,5271,5272],{"class":4407},"{{ label }}",[4299,5274,4739],{"class":4301},[4299,5276,4367],{"class":4305},[4299,5278,4705],{"class":4301},[4299,5280,5281,5283,5285,5288,5290,5292,5294,5296,5298,5300,5302,5305,5307],{"class":4404,"line":4613},[4299,5282,5141],{"class":4301},[4299,5284,4306],{"class":4305},[4299,5286,5287],{"class":4679}," :id",[4299,5289,4683],{"class":4301},[4299,5291,4686],{"class":4420},[4299,5293,5265],{"class":4424},[4299,5295,4686],{"class":4420},[4299,5297,5147],{"class":4679},[4299,5299,4683],{"class":4301},[4299,5301,4686],{"class":4420},[4299,5303,5304],{"class":4424},"value",[4299,5306,4686],{"class":4420},[4299,5308,4705],{"class":4301},[4299,5310,5311,5313,5315],{"class":4404,"line":4619},[4299,5312,5173],{"class":4301},[4299,5314,5004],{"class":4305},[4299,5316,4705],{"class":4301},[4299,5318,5320,5322,5324],{"class":4404,"line":5319},10,[4299,5321,4739],{"class":4301},[4299,5323,5102],{"class":4305},[4299,5325,4705],{"class":4301},[5079,5327,5329,5334,5344],{"icon":5328},"i-logos-react",[4282,5330,5331],{},[5085,5332,5333],{},"React",[4282,5335,5336,5337,5340,5341,5343],{},"Use ",[4294,5338,5339],{},"htmlFor"," instead of ",[4294,5342,4375],{},":",[4395,5345,5349],{"className":5346,"code":5347,"language":5348,"meta":1843,"style":1843},"language-jsx shiki shiki-themes github-light github-light material-theme-palenight","function EmailField() {\n const id = useId()\n return (\n
\n \n \n
\n )\n}\n","jsx",[4294,5350,5351,5356,5361,5366,5371,5376,5381,5386,5391],{"__ignoreMap":1843},[4299,5352,5353],{"class":4404,"line":1766},[4299,5354,5355],{},"function EmailField() {\n",[4299,5357,5358],{"class":4404,"line":1756},[4299,5359,5360],{}," const id = useId()\n",[4299,5362,5363],{"class":4404,"line":1828},[4299,5364,5365],{}," return (\n",[4299,5367,5368],{"class":4404,"line":1749},[4299,5369,5370],{},"
\n",[4299,5372,5373],{"class":4404,"line":1984},[4299,5374,5375],{}," \n",[4299,5377,5378],{"class":4404,"line":4548},[4299,5379,5380],{}," \n",[4299,5382,5383],{"class":4404,"line":4586},[4299,5384,5385],{},"
\n",[4299,5387,5388],{"class":4404,"line":4613},[4299,5389,5390],{}," )\n",[4299,5392,5393],{"class":4404,"line":4619},[4299,5394,5395],{},"}\n",[4286,5397,5399],{"id":5398},"verify-the-fix","Verify the fix",[4343,5401,5402,5408,5414],{},[4346,5403,5404,5407],{},[5085,5405,5406],{},"Re-run Lighthouse"," - The \"Form elements have associated labels\" audit should pass.",[4346,5409,5410,5413],{},[5085,5411,5412],{},"Test with screen reader"," - Tab to each form field and verify the label is announced.",[4346,5415,5416,5419],{},[5085,5417,5418],{},"Click test"," - Clicking a label should focus its associated input.",[4282,5421,5422],{},"Console verification:",[4395,5424,5426],{"className":4397,"code":5425,"language":4399,"meta":1843,"style":1843},"// Should return empty array\n[...document.querySelectorAll('input, select, textarea')].filter((el) => {\n const hasLabel = el.labels?.length > 0\n const hasAria = el.hasAttribute('aria-label') || el.hasAttribute('aria-labelledby')\n return !hasLabel && !hasAria\n})\n",[4294,5427,5428,5433,5474,5496,5540,5556],{"__ignoreMap":1843},[4299,5429,5430],{"class":4404,"line":1766},[4299,5431,5432],{"class":4669},"// Should return empty array\n",[4299,5434,5435,5438,5440,5442,5444,5446,5448,5450,5452,5454,5457,5459,5462,5464,5466,5468,5470,5472],{"class":4404,"line":1756},[4299,5436,5437],{"class":4407},"[",[4299,5439,4886],{"class":4464},[4299,5441,4408],{"class":4407},[4299,5443,4380],{"class":4301},[4299,5445,4414],{"class":4413},[4299,5447,4417],{"class":4407},[4299,5449,4421],{"class":4420},[4299,5451,4425],{"class":4424},[4299,5453,4421],{"class":4420},[4299,5455,5456],{"class":4407},")]",[4299,5458,4380],{"class":4301},[4299,5460,5461],{"class":4413},"filter",[4299,5463,4417],{"class":4407},[4299,5465,4417],{"class":4301},[4299,5467,4443],{"class":4442},[4299,5469,4430],{"class":4301},[4299,5471,4449],{"class":4448},[4299,5473,4452],{"class":4301},[4299,5475,5476,5478,5480,5482,5484,5486,5488,5490,5492,5494],{"class":4404,"line":1828},[4299,5477,4457],{"class":4448},[4299,5479,4461],{"class":4460},[4299,5481,4465],{"class":4464},[4299,5483,4468],{"class":4407},[4299,5485,4380],{"class":4301},[4299,5487,4473],{"class":4407},[4299,5489,4476],{"class":4301},[4299,5491,4479],{"class":4460},[4299,5493,4482],{"class":4464},[4299,5495,4486],{"class":4485},[4299,5497,5498,5500,5503,5505,5507,5509,5511,5513,5515,5517,5519,5521,5524,5526,5528,5530,5532,5534,5536,5538],{"class":4404,"line":1749},[4299,5499,4457],{"class":4448},[4299,5501,5502],{"class":4460}," hasAria",[4299,5504,4465],{"class":4464},[4299,5506,4468],{"class":4407},[4299,5508,4380],{"class":4301},[4299,5510,4502],{"class":4413},[4299,5512,4417],{"class":4505},[4299,5514,4421],{"class":4420},[4299,5516,4385],{"class":4424},[4299,5518,4421],{"class":4420},[4299,5520,4580],{"class":4505},[4299,5522,5523],{"class":4464},"||",[4299,5525,4468],{"class":4407},[4299,5527,4380],{"class":4301},[4299,5529,4502],{"class":4413},[4299,5531,4417],{"class":4505},[4299,5533,4421],{"class":4420},[4299,5535,4389],{"class":4424},[4299,5537,4421],{"class":4420},[4299,5539,4514],{"class":4505},[4299,5541,5542,5545,5547,5549,5551,5553],{"class":4404,"line":1984},[4299,5543,5544],{"class":4551}," return",[4299,5546,4567],{"class":4464},[4299,5548,4561],{"class":4407},[4299,5550,4564],{"class":4464},[4299,5552,4567],{"class":4464},[4299,5554,5555],{"class":4407},"hasAria\n",[4299,5557,5558,5560],{"class":4404,"line":4548},[4299,5559,4622],{"class":4301},[4299,5561,4514],{"class":4407},[4286,5563,5565],{"id":5564},"common-mistakes","Common mistakes",[4356,5567,5568,5574,5592,5602,5612],{},[4346,5569,5570,5573],{},[5085,5571,5572],{},"Placeholder as label",": Placeholders disappear when users type. They are hints, not labels. Always provide a real label.",[4346,5575,5576,5579,5580,5588,5589,5591],{},[5085,5577,5578],{},"Label not associated",": A ",[4294,5581,5582,5584,5586],{"className":4296,"language":4297,"style":1843},[4299,5583,4302],{"class":4301},[4299,5585,4367],{"class":4305},[4299,5587,4309],{"class":4301}," element near the input is not enough. Use ",[4294,5590,4375],{}," attribute or wrap the input inside the label.",[4346,5593,5594,5597,5598,5601],{},[5085,5595,5596],{},"Empty aria-label",": ",[4294,5599,5600],{},"aria-label=\"\""," is worse than no label at all. Always provide meaningful text.",[4346,5603,5604,5607,5608,5611],{},[5085,5605,5606],{},"Labeling hidden inputs",": Inputs with ",[4294,5609,5610],{},"type=\"hidden\""," should not have labels. Screen readers ignore hidden inputs.",[4346,5613,5614,5617,5618,5620],{},[5085,5615,5616],{},"Duplicate IDs",": If two inputs share the same ",[4294,5619,4379],{},", only one gets the label. IDs must be unique per page.",[4286,5622,5624],{"id":5623},"related-issues","Related issues",[4282,5626,5627],{},"Form label issues often appear alongside:",[4356,5629,5630,5636,5642],{},[4346,5631,5632,5635],{},[5633,5634,4122],"a",{"href":4121}," - Submit buttons and form controls both need names",[4346,5637,5638,5641],{},[5633,5639,5640],{"href":4103},"Link name"," - Form action links need descriptive text",[4346,5643,5644,5647],{},[5633,5645,5646],{"href":4127},"Color contrast"," - Low-contrast labels are hard to read",[4286,5649,5651],{"id":5650},"test-your-entire-site","Test your entire site",[4282,5653,5654],{},"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. Greenland",[5656,5657,5658],"style",{},"html pre.shiki code .sx-uw, html code.shiki .sx-uw{--shiki-light:#24292E;--shiki-default:#24292E;--shiki-dark:#89DDFF}html pre.shiki code .sV-QU, html code.shiki .sV-QU{--shiki-light:#22863A;--shiki-default:#22863A;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sqjlB, html code.shiki .sqjlB{--shiki-light:#24292E;--shiki-default:#24292E;--shiki-dark:#BABED8}html pre.shiki code .s0YkB, html code.shiki .s0YkB{--shiki-light:#6F42C1;--shiki-default:#6F42C1;--shiki-dark:#82AAFF}html pre.shiki code .sbw7o, html code.shiki .sbw7o{--shiki-light:#032F62;--shiki-default:#032F62;--shiki-dark:#89DDFF}html pre.shiki code .sJnJ8, html code.shiki .sJnJ8{--shiki-light:#032F62;--shiki-default:#032F62;--shiki-dark:#C3E88D}html pre.shiki code .sgUNn, html code.shiki .sgUNn{--shiki-light:#E36209;--shiki-light-font-style:inherit;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .swqme, html code.shiki .swqme{--shiki-light:#D73A49;--shiki-default:#D73A49;--shiki-dark:#C792EA}html pre.shiki code .smpaK, html code.shiki .smpaK{--shiki-light:#005CC5;--shiki-default:#005CC5;--shiki-dark:#BABED8}html pre.shiki code .sc1V3, html code.shiki .sc1V3{--shiki-light:#D73A49;--shiki-default:#D73A49;--shiki-dark:#89DDFF}html pre.shiki code .sjz_z, html code.shiki .sjz_z{--shiki-light:#005CC5;--shiki-default:#005CC5;--shiki-dark:#F78C6C}html pre.shiki code .sqVJQ, html code.shiki .sqVJQ{--shiki-light:#24292E;--shiki-default:#24292E;--shiki-dark:#F07178}html pre.shiki code .smL2f, html code.shiki .smL2f{--shiki-light:#D73A49;--shiki-light-font-style:inherit;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTBSN, html code.shiki .sTBSN{--shiki-light:#6A737D;--shiki-light-font-style:inherit;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sg-iE, html code.shiki .sg-iE{--shiki-light:#6F42C1;--shiki-default:#6F42C1;--shiki-dark:#C792EA}html pre.shiki code .sFfpx, html code.shiki .sFfpx{--shiki-light:#B31D28;--shiki-light-font-style:italic;--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#F07178;--shiki-dark-font-style:inherit}",{"title":1843,"searchDepth":1756,"depth":1756,"links":5660},[5661,5662,5666,5673,5674,5675,5676,5677],{"id":4288,"depth":1756,"text":4289},{"id":4335,"depth":1756,"text":4336,"children":5663},[5664,5665],{"id":4340,"depth":1828,"text":4341},{"id":4627,"depth":1828,"text":4628},{"id":4642,"depth":1756,"text":4643,"children":5667},[5668,5670,5671,5672],{"id":4646,"depth":1828,"text":5669},"1. Use with for attribute",{"id":4777,"depth":1828,"text":4778},{"id":4839,"depth":1828,"text":4840},{"id":4960,"depth":1828,"text":4961},{"id":5076,"depth":1756,"text":5077},{"id":5398,"depth":1756,"text":5399},{"id":5564,"depth":1756,"text":5565},{"id":5623,"depth":1756,"text":5624},{"id":5650,"depth":1756,"text":5651},"Learn how to fix missing form labels in Lighthouse accessibility audits. Labels ensure form controls are announced properly by assistive technologies.","md","i-heroicons-wrench-screwdriver",[4367,5682,5683,5684,5685],"form labels","accessibility","screen reader","form controls",{"tags":5687},[5683,5688],"lighthouse",{"title":4101},null,"4 min",[5693,5696],{"path":5694,"title":5695},"/learn-lighthouse/accessibility#common-accessibility-issues","All Accessibility Issues",{"path":4086,"title":5697},"Accessibility Overview",{"title":4277,"description":5678},{"loc":4100,"lastmod":313},"learn-lighthouse/accessibility/12.label","WgDBEuX_FIjJoIrapypRPZI1mPyEsbNToFZVzhBEFes",[5703,5706],{"title":4098,"path":4097,"stem":5704,"description":5705,"children":-1,"_path":4097},"learn-lighthouse/accessibility/11.image-alt","Learn how to fix missing image alt attributes in Lighthouse accessibility audits",{"title":4104,"path":4103,"stem":5707,"description":5708,"children":-1,"_path":4103},"learn-lighthouse/accessibility/13.link-name","Fix links without discernible names in Lighthouse accessibility audits",["Reactive",5710],{"$scolor-mode":5711,"$snuxt-seo-utils:routeRules":5713,"$stoasts":5714,"$snuxt-seo:breadcrumb:breadcrumb":5715,"$ssite-config":5722},{"preference":5712,"value":5712,"unknown":4544,"forced":3855},"system",{"head":-1,"seoMeta":-1},[],[5716,5720,5721],{"to":5717,"icon":5718,"label":5719,"ariaLabel":5719,"current":3855},"/learn-lighthouse","i-heroicons-academic-cap","Learn Google Lighthouse",{"to":4086,"label":4081,"ariaLabel":4081,"current":3855},{"to":4086,"label":4087,"ariaLabel":4087,"current":3855},{"_priority":5723,"description":5726,"env":5727,"name":5728,"titleSeparator":5729,"url":5730},{"env":5724,"url":5725,"name":5725,"description":5725,"titleSeparator":5725},-15,-3,"Google Lighthouse for your entire site.","production","Unlighthouse","·","https://unlighthouse.dev",["Set"],["ShallowReactive",5733],{"stats":-1,"search":-1,"navigation":-1,"learn-nav":-1,"learn-/learn-lighthouse/accessibility/label":-1,"learn-/learn-lighthouse/accessibility/label-surround":-1}]