@foreach
Iterates an array or collection in a template and exposes loop metadata through $loop.
@foreach ($array as $key => $value) ... @endforeachParameters
| Parameter | Type | Description |
|---|---|---|
$array | iterable | The array, Collection or Traversable to walk. Written as the full PHP foreach expression, including the `as` clause. |
$keyoptional | mixed | Optional key variable, declared as `$key =>` before the value variable. |
$value | mixed | The variable bound to the current element inside the block. |
Return value
Nothing — the block is emitted once per element of the iterable.
Errors
Iterating a value that is not an array or Traversable raises a PHP warning and renders nothing; a missing @endforeach produces a ParseError in the compiled view.
@foreach compiles to PHP's foreach, with one addition: Blade pushes a $loop object onto a stack for every iteration, so a template can ask where it is without counting by hand.
| Property | Description |
|---|---|
$loop->index | The index of the current loop iteration (starts at 0). |
$loop->iteration | The current loop iteration (starts at 1). |
$loop->remaining | The iterations remaining in the loop. |
$loop->count | The total number of items in the array being iterated. |
$loop->first | Whether this is the first iteration through the loop. |
$loop->last | Whether this is the last iteration through the loop. |
$loop->even / $loop->odd | Whether this is an even or odd iteration. |
$loop->depth | The nesting level of the current loop. |
$loop->parent | When in a nested loop, the parent's loop variable. |
Skipping and stopping
@continue and @break work as in PHP, and both accept the condition inline: @continue($user->type == 1). That reads better than wrapping the directive in an @if.
What trips people up
The loop is a rendering construct, not a query. Calling a relation inside the block gives you one query per row; eager load it in the controller instead. And when the collection may be empty, @forelse saves you an enclosing conditional.
Example
<?php
use Illuminate\Support\Facades\Blade;
$template = <<<'BLADE'
@foreach ($users as $user)
- {{ $loop->iteration }}/{{ $loop->count }} {{ $user }}{{ $loop->last ? ' (last)' : '' }}
@endforeach
BLADE;
echo Blade::render($template, ['users' => ['Ada', 'Grace', 'Katherine']]);
// - 1/3 Ada
// - 2/3 Grace
// - 3/3 Katherine (last)When to use it
- Render one table row per order, striping rows with $loop->even
- Add a separator between items but not after the last one
- Walk a nested menu and highlight the first item of the parent group
- Skip soft-blocked users without a wrapping conditional using @continue
Frequently asked questions
How do I check for the first or last item in a Blade foreach?
Use $loop->first and $loop->last inside the block — Blade defines the $loop object on every iteration. $loop->iteration counts from 1 and $loop->index counts from 0.
How do I access the outer loop from a nested Blade foreach?
Use $loop->parent, which holds the enclosing loop's variable; it is null at the outermost level. $loop->depth reports the current nesting level, starting at 1.
Can I skip an item inside a Blade foreach?
Yes — @continue skips to the next iteration and @break exits the loop. Both accept the condition inline, as in @continue($user->type == 1), so no wrapping @if is needed.
More examples
Nested loops and $loop->parent
Inside the inner loop $loop refers to the posts, reporting depth 2, while $loop->parent refers to the users loop and reports depth 1. This is the documented way to ask a question about the outer iteration from within an inner one; without it you would have to copy $loop into a differently named variable before descending.
<?php
// resources/views/users/index.blade.php
$view = <<<'BLADE'
@foreach ($users as $user)
<h2>{{ $user->name }} (depth {{ $loop->depth }})</h2>
@foreach ($user->posts as $post)
@if ($loop->parent->first)
<em>From the first user in the list</em>
@endif
<p>{{ $post->title }} — inner depth {{ $loop->depth }}</p>
@endforeach
@endforeach
BLADE;
echo view('users.index', ['users' => $users])->render();Inline @continue and @break conditions
Ada is skipped by the inline @continue, and the loop stops after Katherine because @break's condition matched — Dorothy is never rendered. Passing the condition to the directive is equivalent to wrapping it in @if, with two fewer lines per guard.
<?php
use Illuminate\Support\Facades\Blade;
$template = <<<'BLADE'
@foreach ($users as $user)
@continue($user['type'] === 1)
- {{ $user['name'] }}
@break($user['number'] === 5)
@endforeach
BLADE;
echo Blade::render($template, ['users' => [
['name' => 'Ada', 'type' => 1, 'number' => 1],
['name' => 'Grace', 'type' => 2, 'number' => 4],
['name' => 'Katherine', 'type' => 2, 'number' => 5],
['name' => 'Dorothy', 'type' => 2, 'number' => 9],
]]);
// - Grace
// - KatherineEager load before the loop
Without the with('customer') call, the same template issues one extra query per rendered row — twenty rows become twenty-one queries. The template does not change; the fix belongs in the controller. This is the most common performance problem introduced by an otherwise correct @foreach block.
<?php
use App\Models\Order;
// Controller: one query for orders, one for every customer.
$orders = Order::with('customer')->latest()->take(20)->get();
// resources/views/orders/index.blade.php
$view = <<<'BLADE'
@foreach ($orders as $order)
<tr>
<td>{{ $order->number }}</td>
<td>{{ $order->customer->name }}</td>
</tr>
@endforeach
BLADE;
echo view('orders.index', ['orders' => $orders])->render();
Discussion