Blade automatically injects a special $loop variable inside every @foreach loop, giving access to the current index, first/last flags, and iteration count without needing to manually declare and increment a counter variable yourself.
The basic properties
@foreach ($products as $product)
Index: {{ $loop->index }} (0-based)
Iteration: {{ $loop->iteration }} (1-based)
{{ $product->name }}
@endforeach
Checking for the first or last iteration
@foreach ($products as $product)
<div class="{{ $loop->first ? 'first-item' : '' }} {{ $loop->last ? 'last-item' : '' }}">
{{ $product->name }}
</div>
@endforeach
$loop->first and $loop->last are genuinely useful for conditional styling (removing a border on the last item in a list, adding special styling to the first) without needing to manually compare the current index against the collection's count.
Getting the total item count
@foreach ($products as $product)
Item {{ $loop->iteration }} of {{ $loop->count }}
@endforeach
Checking for even/odd rows, for alternating table styling
@foreach ($products as $product)
<tr class="{{ $loop->even ? 'bg-gray-50' : '' }}">
<td>{{ $product->name }}</td>
</tr>
@endforeach
Accessing the parent loop from a nested @foreach
@foreach ($categories as $category)
{{ $category->name }}
@foreach ($category->products as $product)
Category {{ $loop->parent->iteration }}, Product {{ $loop->iteration }}: {{ $product->name }}
@endforeach
@endforeach
$loop->parent is what makes the outer loop's own $loop instance available from inside a nested loop, where $loop would otherwise only refer to the inner loop — necessary any time nested loop logic needs to reference the outer iteration's position.
Checking remaining iterations
@foreach ($items as $item)
{{ $item->name }}
@if ($loop->remaining > 0)
(more items follow)
@endif
@endforeach
Why $loop is Blade-specific, not a PHP feature
$loop doesn't exist in plain PHP foreach loops at all — it's a Blade compilation feature, automatically injected only when using the @foreach directive specifically, not when using a raw foreach written directly in <?php ?> tags inside a Blade file.
$loop also tells you depth inside nested loops
When a component or template renders recursive-looking nested structures, $loop->depth tells you how deeply nested the current Blade loop is. That can be useful for indentation or data attributes without manually passing a level counter through every partial. Use it for presentation state, not as a substitute for understanding the underlying tree structure.
empty collections have a dedicated Blade pattern
If the view needs an empty state, @forelse is usually cleaner than wrapping @foreach in a separate count check:
@forelse ($products as $product)
<div>
{{ $loop->iteration }}. {{ $product->name }}
</div>
@empty
<p>No products found.</p>
@endforelse
The $loop variable is available in the iteration branch exactly as it is in @foreach.
Do not use $loop to trigger database work
A tempting pattern is "on the first iteration, load something extra" or calling a relationship for every row while using $loop to format the result. The loop metadata itself is cheap; the hidden problem is database access from the view. Eager-load relationships in the controller/query layer before rendering so a neat Blade loop does not become an N+1 query source.
$loop indexes describe rendered iteration order, not model identity
$loop->index and iteration reset every time the template loops. They are appropriate for display numbering and alternating styles, but not for persistent DOM IDs, form field identity, or business keys. Use the model's stable identifier for those purposes; pagination, sorting, or filtering can change a loop position without changing the underlying record.