ADA Compliance Professionals

    Unique ID values for ARIA and labels

    Last updated:

    Who it helps:
    Blind
    Cognitive
    Standard:
    WCAG 2.2 Level A

    ID values used by ARIA and labels must not repeat within a document

    Every id value targeted by for, aria-labelledby, aria-describedby, aria-controls, and similar attributes must be unique in the DOM. Duplicate IDs can misroute labels and descriptions or attach only to the first matching element. This harms screen reader users and breaks label click-to-focus behavior for everyone.

    Why It Matters

    Assistive technologies depend on unique IDs to bind labels, hints, and error messages to the correct control. When two elements share the same ID, only the first one may receive the association, leaving others unnamed or misnamed.

    People using screen readers may hear the wrong label or none at all. Users with cognitive disabilities rely on clear, consistent labels and instructions. Duplicate IDs also cause scripts and CSS selectors to behave unpredictably.

    HTML requires IDs to be unique. While WCAG 2.2 no longer includes 4.1.1 Parsing, duplicate IDs can still lead to failures where relationships and names are not programmatically determined (e.g., 1.3.1 Info and Relationships, 4.1.2 Name, Role, Value).

    Common Causes

    • Copy-pasting components or form fields with the same id value
    • Reusing template markup where static IDs are not parameterized
    • Multiple modals or widgets instantiated with identical internal IDs
    • Client/server rendering or hydration that regenerates conflicting IDs
    • Using IDs for styling/JS hooks on repeated items instead of classes/data-attributes
    • Duplicated error/help elements where aria-describedby points to the same ID across fields

    How to Fix

    • Inventory and rename duplicates:
    • Search the DOM for repeated id values and change each to a unique value.
    • Parameterize component IDs:
    • In reusable components, accept an id prop or generate a unique suffix (e.g., prefix + instance counter/UUID).
    • Reference the right target:
    • Ensure for, aria-labelledby, aria-describedby, aria-controls, and aria-owns point to existing, unique IDs.
    • Prefer classes for repeating hooks:
    • Use class or data-* attributes for styling and JS on lists/grids; reserve id for single, unique elements.
    • Keep IDs stable across renders:
    • Avoid regenerating IDs on each render; generate once per instance to preserve associations.
    • Validate build and runtime:
    • Check server-rendered HTML and client-side state after user interactions (added rows, modals, tabs) for new duplicates.

    Recommendation: Use a consistent scheme such as componentName-fieldName--uniqueSuffix (letters, digits, hyphens only) and ensure uniqueness per document.

    How to Test

    • Automated check:
    • Run axe, WAVE, or Lighthouse to flag Duplicate ID issues.
    • Validate HTML (Nu HTML Checker) for non-unique IDs and broken references.
    • DevTools/console check:
    • Query duplicates:
    JS
        const ids = [...document.querySelectorAll('[id]')].map(n => n.id);
        const dupes = [...new Set(ids.filter((id, i) => ids.indexOf(id) !== i))];
        console.log('Duplicate IDs:', dupes);
    - Keyboard check:
      - Tab through form fields. Click each visual label and confirm focus moves to the intended control.
    - Screen reader check (NVDAJAWSVoiceOver):
      - Navigate by form fields. Verify each control’s label is announced correctly; confirm helperror text is read for the right field.
    - Mobiletouch check (TalkBackVoiceOver):
      - Flick through controls. Ensure labels and descriptions are announced for the correct element.

    Checklist:

    • No duplicate id values exist in the DOM.
    • Every for/aria-* reference points to exactly one element.
    • Labels, hints, and errors are announced for the correct control.

    Good Example

    A form with unique IDs and properly scoped descriptions.

    HTML
    <form>
      <div>
        <label for="contact-email">Email</label>
        <input id="contact-email" name="email" type="email" aria-describedby="contact-email-hint" />
        <div id="contact-email-hint">Use a work email if possible.</div>
      </div>
    
      <div>
        <label for="contact-phone">Phone</label>
        <input id="contact-phone" name="phone" type="tel" aria-describedby="contact-phone-hint" />
        <div id="contact-phone-hint">Digits only, no dashes.</div>
      </div>
    </form>

    Bad Example

    Duplicate IDs cause labels and descriptions to bind incorrectly.

    HTML
    <form>
      <div>
        <label for="name">Full name</label>
        <input id="name" name="firstName" type="text" />
      </div>
    
      <div>
        <label for="name">Emergency contact</label>
        <input id="name" name="emergencyContact" type="text" aria-describedby="help" />
        <p id="help">Enter first and last name.</p>
      </div>
    </form>

    Quick Checklist

    • Each id value appears only once per document
    • Labels use for and match a unique input id
    • aria-labelledby and aria-describedby target unique, existing elements
    • Components generate scoped, conflict-free IDs
    • Do not use id for styling/JS on repeated items
    • Validate HTML and run automated accessibility checks
    • Keep IDs stable across renders and state changes