The HTML5 spec requires the id attribute to be unique per document — no two elements may share the same id. When duplicates appear, document.getElementById returns only the first match, <label for="..."> may bind to the wrong control, and ARIA references like aria-labelledby point to the wrong element. The result is silent breakage of JavaScript, accessibility, and in-page anchor links.
| Failure mode | What breaks | User impact |
|---|---|---|
| document.getElementById(id) | Returns only the first match | JS targeting the second element silently does nothing |
| <label for="id"> | Associates with the first matching control | Clicking label focuses wrong field; screen reader announces wrong label (WCAG 1.3.1 fail) |
| aria-labelledby / aria-describedby | Resolves to the first matching id | Screen reader announces wrong description for buttons or controls |
| <a href="#id"> | Browser jumps to first occurrence | In-page anchors and skip links land at the wrong section |
| CSS :target selector | Matches the first occurrence only | Styling tied to URL fragments behaves unpredictably |
| Form submission with same name | (separate from id, but often paired) | Wrong field value submitted, autofill misbehaves |
A label binding to the wrong input is the most common duplicate-id bug. Here is the broken version followed by the fix.
<!-- BAD: two inputs share id="email" -->
<form>
<label for="email">Contact email</label>
<input id="email" name="contact_email" type="email">
</form>
<form>
<label for="email">Newsletter email</label>
<input id="email" name="newsletter_email" type="email">
</form>
<!-- GOOD: each id is unique -->
<form>
<label for="contact-email">Contact email</label>
<input id="contact-email" name="contact_email" type="email">
</form>
<form>
<label for="newsletter-email">Newsletter email</label>
<input id="newsletter-email" name="newsletter_email" type="email">
</form>Array.from(document.querySelectorAll('[id]'))
.map(el => el.id)
.filter((id, i, a) => a.indexOf(id) !== i);Yes. The HTML5 spec states the id attribute must be unique among all the IDs in the element's tree.
It returns the first element in tree order that has the matching id. The other elements are silently ignored.
Not directly as a ranking signal. But broken in-page anchors, broken accessibility, and JS errors all degrade UX, which can affect engagement metrics.
When they cause incorrect label or ARIA associations, yes — WCAG 1.3.1 (Info and Relationships) and 4.1.1 historically. WCAG 2.2 removed the standalone parsing requirement, but the underlying associations still must be correct.
Use the useId hook from React 18+. It produces stable, unique ids that are safe across server and client rendering.
No. Shadow DOM creates a separate tree, so ids inside it do not collide with the main document.
It returns all matches. But most code uses getElementById, which returns only the first.
No. The behavior is specified and changing it would break existing sites. Fix the duplicates instead.
Indirectly, yes. AI search engines (ChatGPT, Perplexity, Google AI Overviews) preferentially cite well-ranked, well-structured pages, and duplicate ids confuse the accessibility tree and DOM landmarks those systems rely on to extract content. When labels and ARIA references resolve to the wrong element, AI extractors may pull mislabeled or mismatched answers from your page, lowering your odds of being cited cleanly.
Make every id unique. Run the one-line console snippet above to find duplicates today, then fix them by scoping names to context or generating ids with useId. Confirm with the "[id] attributes on the page are unique" audit and Chrome DevTools Issues panel. Run a Greadme deep scan to surface duplicate ids across your entire site in one pass and catch the templates and components that are the real source of regressions.