Duplicate IDs: A Small Markup Mistake With Outsized Consequences
An id attribute exists to uniquely identify one element on a page — nothing else is supposed to share it. When two elements do share an id (often from a copy-pasted component, a repeated list item template, or a CMS that renders the same block twice), any code or markup referencing that id becomes ambiguous: browsers and assistive technology typically resolve it to whichever matching element comes first in the DOM, silently ignoring the rest.
This is WCAG Success Criterion 4.1.1 (Parsing) in WCAG 2.0/2.1 — worth noting that 4.1.1 was removed entirely in WCAG 2.2, since modern browsers' error-recovery parsing makes most raw markup-validity issues moot. Duplicate IDs specifically, though, still cause real functional failures under 4.1.2 (Name, Role, Value) whenever the duplicated ID is actually relied upon for a label, ARIA reference, or fragment link — the removal of 4.1.1 doesn't mean duplicate IDs became harmless, just that "the HTML doesn't technically validate" stopped being the failure mode that matters.
Where this actually breaks things
A <label for="..."> matching a duplicated id — the label may bind to the wrong input, or to whichever one happens to come first, leaving the other completely unlabeled from a screen reader's perspective even though the markup looks correct at a glance.
<!-- Two form fields on the page, both id="email" from a copy-pasted block -->
<label for="email">Billing email</label>
<input id="email" name="billing_email">
<label for="email">Shipping email</label>
<input id="email" name="shipping_email">
<!-- Both labels may bind to the FIRST input only -->
ARIA attributes like aria-labelledby or aria-describedby that reference an id — if that id isn't unique, the reference resolves unpredictably, and the accessible name or description assistive technology announces may not match what a sighted user sees.
In-page anchor links (<a href="#section-2">) — with a duplicated target id, the browser jumps to the first match, which may not be the one the link's visible text promised.
How to fix it
Make every id genuinely unique across the whole page, not just within a single component's local scope. This most often means auditing components that get rendered multiple times on one page (a card repeated in a list, a modal template reused for several different dialogs) and switching from a hardcoded id to one that incorporates something unique per instance — an item's database key, or a component-generated unique suffix.
<!-- Before: hardcoded id, breaks when the component repeats -->
<div id="card-title">...</div>
<!-- After: unique per instance -->
<div id={`card-title-${item.id}`}>...</div>
This is a mechanical fix once found, but it can be easy to miss visually since nothing looks wrong on screen — it only surfaces as a functional failure for whichever assistive-technology-dependent feature happens to rely on the broken reference.
Official references
Common questions
- Are duplicate IDs still a WCAG failure in WCAG 2.2?
- In practice, yes. SC 4.1.1 (Parsing) was removed in WCAG 2.2, but a duplicated `id` that a label, ARIA reference, or anchor link relies on still fails 4.1.2 (Name, Role, Value) or 1.3.1, because the reference resolves to the wrong element.
- Why do duplicate IDs break screen readers?
- When two elements share an `id`, references to it are ambiguous. Browsers and assistive technology typically resolve to the first match in the DOM and ignore the rest, so a label or description can attach to the wrong control.
- How do I fix duplicate IDs?
- Make every `id` unique across the whole page. For components rendered more than once, replace hardcoded IDs with ones that include a per-instance value, such as a database key or a generated unique suffix.
Related articles
Want to see how your own site scores?
Run a free accessibility scan