axe-core duplicate-id-active violation breaks focus order
Fixes the axe-core duplicate-id-active violation by making duplicated ids on focusable elements unique. Use it when axe reports multiple active elements sharing an id. Not for duplicate ids on inert decorative elements, which this rule ignores.
axe-core duplicate-id-active violation breaks focus order - how to fix it
TL;DR
Make every id unique: the rule only fires when a duplicated id is on an active (focusable or referenced) element. Search for the duplicated id, rename or scope the copies, and re-run axe. One line of why: getElementById and label and aria references resolve to the first match, so duplicates silently wire labels and focus to the wrong element.
The error, verbatim
{
"id": "duplicate-id-active",
"impact": "serious",
"help": "IDs of active elements must be unique",
"nodes": [
{ "target": ["#submit-btn"], "failureSummary": "Fix any of the following: Document has multiple active elements with the same id attribute: submit-btn" }
]
}Fix it step by step
Step 1: Reproduce on one page
npx @axe-core/cli https://example.com --rules duplicate-id-active --save axe-dup.jsonExpected: Violations array contains duplicate-id-active with the duplicated id in the failure summary.
Step 2: Extract every duplicated id
node -e "const r=require('./axe-dup.json'); r.violations.forEach(function(v){v.nodes.forEach(function(n){console.log(n.failureSummary.match(/same id attribute: (.+)/)[1])})})"Expected: Prints the duplicated id values, like submit-btn.
Step 3: Find the copies in source
rg -n 'submit-btn' src --glob '*.{jsx,tsx,vue,html}' | head -20Expected: Shows each file and line rendering the same id, usually a component rendered twice.
Step 4: Deduplicate and re-scan
npx @axe-core/cli https://example.com --rules duplicate-id-activeExpected: Exit code 0, 0 violations. Tab through the page to confirm focus lands on the right control each time.
Step 5: Gate the rule in CI
npx @axe-core/cli https://example.com --exit | tail -3Expected: Non-zero exit while any violation remains; add this command to CI so the fix never regresses.
When to use this skill
- Your axe-core report lists this exact rule id under violations
- You are clearing automated WCAG 2.1 AA failures before a release or audit
- A CI a11y gate (pa11y-ci, lighthouse CI, cypress-axe) is red because of this rule
When NOT to use this skill
- The issue only shows up in manual screen-reader testing and axe reports zero violations for the rule
- You are doing a full manual WCAG audit, this skill covers the single automated rule only
- The page is a third-party embed you cannot edit, flag it to the vendor instead
Compatibility
axe-core 4.8+ (rule duplicate-id-active, WCAG 4.1.1). Same check in axe DevTools and pa11y. Pin the tool version in the lockfile so scans stay reproducible across machines.
Variant phrasings
document has multiple active elements with the same id
The failureSummary wording, same dedupe fix.
duplicate id breaks label association axe
The common downstream symptom: a label points at the wrong input because ids collided.
axe DevTools flags the same rule
The browser extension runs the same rule engine; fix once and it clears in every runner.
Why it happens
Components with hardcoded ids (submit-btn, email-input) break the moment they render twice on a page, modals, repeated cards, and portaled dialogs are the classic sources. The rule only flags active elements (focusable or referenced by aria), so purely decorative duplicates slip through while the ones that break focus and label wiring get caught. React and Vue do not warn about this, so it ships silently. The same violation usually repeats on every page built from the same template, so fix the component or template once instead of patching pages. After the fix, re-scan the whole site, not just the one page, to confirm the template-level change cleared them all.
Edge cases
- Use useId or a unique-id helper in components instead of hand-writing ids, it kills the whole class of bug.
- Duplicated ids inside display none subtrees can still fail if the elements are active, hiding is not deduping.
- Third-party widgets that inject fixed ids need a wrapper that rewrites the ids after mount.
- Fix every instance of the rule before moving on; a half-fixed rule across templates re-fails the next full scan.
Provenance
Resolved from the public thread: https://vectle.com/posts/pstsOp61NnWlWWJ8u3WWIWyw
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.