Laravel Blade Foreach Loop: Complete Guide with the $loop Variable and @forelse
Maksudur Rahman
Iterating through collections and arrays in Laravel Blade templates is more powerful than native PHP foreach. Blade provides syntactic shortcuts, empty collection fallbacks, and the automatic $loop variable.
Here is the complete guide to mastering loops in Blade.
1. Basic @foreach Syntax
The standard Blade loop:
<ul>
@foreach ($users as $user)
<li>{{ $user->name }} ({{ $user->email }})</li>
@endforeach
</ul>2. Handling Empty Collections with @forelse
Avoid wrapping @foreach inside an @if(count($items)) check. Blade provides @forelse to display an empty state cleanly:
<div class="user-list">
@forelse ($users as $user)
<div class="user-card">
<h3>{{ $user->name }}</h3>
<p>{{ $user->role }}</p>
</div>
@empty
<div class="empty-state">
<p>No active users found.</p>
</div>
@endforelse
</div>3. The Magic $loop Variable
Inside every @foreach and @forelse block, Laravel automatically injects a $loop object. You do not need to initialize a manual $i = 0 counter:
Property | Description | Example Use Case |
|---|---|---|
| Zero-based index (0, 1, 2...) | CSS grid calculation |
| 1-based index (1, 2, 3...) | Numbered lists, table rows |
| Number of items remaining | Countdown displays |
| Total number of items | "Item 3 of 10" |
| Boolean, | Highlight top result / banner |
| Boolean, | Skip divider line on last item |
| Boolean, | Zebra striping table rows |
| Boolean, | Alternating styling |
| Integer depth level of nested loop | Multi-level tree indentation |
| Access parent | Combined counters in child tables |
Practical Example: Table Zebra Striping and Badges
<table>
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Status</th>
</tr>
</thead>
<tbody>
@forelse ($orders as $order)
<tr class="{{ $loop->even ? 'bg-gray-50' : 'bg-white' }}">
<td>{{ $loop->iteration }}</td>
<td>{{ $order->customer_name }}</td>
<td>
@if($loop->first)
<span class="badge badge-primary">Latest Order</span>
@endif
{{ $order->status }}
</td>
</tr>
@empty
<tr>
<td colspan="3">No orders found.</td>
</tr>
@endforelse
</tbody>
</table>4. Nested Loops and $loop->parent
When looping through categories and their products, use $loop->parent to check the outer iteration:
@foreach ($categories as $category)
<h2>{{ $loop->iteration }}. {{ $category->name }}</h2>
<ul>
@foreach ($category->products as $product)
<li>
Category {{ $loop->parent->iteration }}, Product {{ $loop->iteration }}:
{{ $product->name }}
</li>
@endforeach
</ul>
@endforeach5. Performance: Avoid N+1 Queries in Blade Loops
The most common bug in Blade loops is querying relations inside the iteration:
{{-- BAD: Triggers a query for every single post (N+1 Problem) --}}
@foreach ($posts as $post)
<p>{{ $post->author->name }}</p>
@endforeachAlways eager load relationships in your controller before passing data to Blade:
// In Controller:
$posts = Post::with('author')->get();
return view('posts.index', compact('posts'));