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)
{{ $product->name }}
@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)
{{ $product->name }}
@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 tags inside a Blade file.