Where Duplicate IDs Come From
- A hardcoded id inside a reusable component. A modal, tab panel, or form field component with
id="email-input"baked in produces a collision the instant it's rendered more than once on a page. - Server-rendered fragments concatenated together. Two independently-built page sections (a header widget plus a CMS block, for example) each define their own
id="cta"without knowing about the other. - Copy-pasted markup during a redesign. A section gets duplicated for an A/B test or a new variant, and the ids come along for the ride unchanged.
What Actually Breaks
- Form labels.
<label for="email">only ever associates with the firstid="email"in the DOM - clicking a second label with the same target focuses the wrong field, or nothing at all. - In-page anchors. A nav link to
#pricingjumps to whicheverid="pricing"comes first, even if the one you meant to link to is further down the page. - JavaScript selectors.
document.getElementById()andquerySelector("#id")both return only the first match - click handlers, state bindings, or accessibility attributes (aria-labelledby) can silently attach to the wrong element.
How to Fix It
1. Never hardcode an id inside a component that renders more than once
1import { useId } from "react";
2
3function FormField({ label }: { label: string }) {
4 const id = useId();
5 return (
6 <div>
7 <label htmlFor={id}>{label}</label>
8 <input id={id} />
9 </div>
10 );
11}2. Prefer classes for styling, ids only for genuinely unique targets
If an id is only there to be a CSS hook, it almost certainly should be a class instead - classes are meant to repeat, ids aren't.
3. Audit merged/concatenated page sections
When a page is assembled from independently maintained fragments (a CMS block plus a hardcoded header, for example), grep each fragment's ids against the others before shipping - this is the collision source that's hardest to catch by just reading one file in isolation.
How Scanverra Detects This
Scanverra's browser test crawls the live rendered DOM - not just the raw HTML source - and collects every id attribute on the page, flagging any value that appears more than once so you get the exact duplicated id rather than having to hunt for it manually.