All rules

Sheath rule

best-practices-no-duplicate-id

Disallow duplicate id attributes across the document.
Package
Core
Default severity
error by default
Auto-fix
Manual fix

#Why

An id is meant to identify one element, so everything that resolves one takes the first match and ignores the rest:

  • getElementById and querySelector('#x') return the first
  • <label for="x"> labels the first, leaving the other input unlabeled
  • aria-labelledby and aria-describedby point at the first
  • #x in a URL scrolls to the first

#Examples

#Bad

<!-- Same ID used twice -->
<div id="header">Site Header</div>
<div id="header">Page Header</div>
<!-- ID duplicated across different element types -->
<input type="text" id="email">
<label id="email">Email</label>
<!-- ID reused in loops (common mistake in templates) -->
@foreach($items as $item)
<div id="item">{{ $item->name }}</div>
@endforeach

#Good

<!-- Unique IDs -->
<div id="site-header">Site Header</div>
<div id="page-header">Page Header</div>
<!-- Different IDs -->
<input type="text" id="email-input">
<label id="email-label" for="email-input">Email</label>
<!-- Dynamic IDs in loops -->
@foreach($items as $item)
<div id="item-{{ $item->id }}">{{ $item->name }}</div>
@endforeach

#Notes

  • Empty IDs are ignored
  • The first occurrence is valid; subsequent duplicates are flagged
  • A static ID inside a potentially repeating Blade loop is flagged even when it appears only once in the source; the @empty arm of @forelse is not repeating
  • Ids on mutually exclusive branches do not count against each other: @if ($editing) <div id="form"> @else <div id="form"> @endif renders exactly one of the two, so it is not reported. Repeats inside the same branch, or against an unconditional element, still are
  • Use classes instead of IDs when you need to select multiple elements
  • In Blade templates, use dynamic IDs with unique values

#References

#Related Rules