Why Islands Matter in Livewire v3
A full-page Livewire component tree hydrates every component on every request. For dashboards with a dozen widgets, that means twelve round-trips of PHP work before the user sees anything interactive. Livewire v3 solves this with lazy components — components that render a placeholder on the initial page load and hydrate asynchronously over a subsequent HTTP request.
This is the "islands" mental model: static HTML is the sea, and interactive Livewire components are islands that boot independently.
The lazy Attribute
Making a component lazy is a single attribute on the <livewire: tag:
{{-- resources/views/dashboard.blade.php --}}
<livewire:revenue-chart lazy />
<livewire:recent-orders lazy />
<livewire:support-queue lazy />
Each component fires a separate GET request after the page loads, hydrates in isolation, and swaps its placeholder. The initial HTML response is tiny and fast.
Providing a Placeholder
Without a placeholder, Livewire renders nothing until the component boots. Define one inside the component class:
<?php
namespace App\Livewire;
use Livewire\Component;
class RevenueChart extends Component
{
public function placeholder(): string
{
return <<<'HTML'
<div class="animate-pulse h-48 bg-gray-100 rounded-xl"></div>
HTML;
}
public function render()
{
return view('livewire.revenue-chart', [
'data' => $this->loadChartData(),
]);
}
private function loadChartData(): array
{
// expensive query — safe to run lazily
return RevenueAggregator::monthly();
}
}
The placeholder is rendered server-side on the first pass, so it participates in SSR and is immediately visible — no layout shift.
Passing Data Into Lazy Components
Props are serialised into the lazy request automatically. Pass them as normal:
<livewire:order-table :team-id="$team->id" lazy />
class OrderTable extends Component
{
public int $teamId;
public function mount(int $teamId): void
{
$this->teamId = $teamId;
}
}
Security note: Props are included in the signed snapshot. They are tamper-evident but not secret. Never pass sensitive tokens as props.
Deferred Loading vs. Lazy Components
These two terms are often conflated. In Livewire v3:
- Lazy component — the entire component boots on a deferred request. The component class is not instantiated on the initial render.
- Deferred property / action — a property or method call that is batched and sent after the initial render, within an already-hydrated component.
For a widget that is always on screen but has one slow sub-query, a deferred property is cleaner than making the whole component lazy:
use Livewire\Attributes\Lazy;
class SupportQueue extends Component
{
#[\Livewire\Attributes\Computed(persist: true)]
public function tickets()
{
return Ticket::open()->with('assignee')->latest()->get();
}
}
For widgets that may never be scrolled into view (below the fold), full lazy loading wins.
Conditional Lazy Loading
You can make laziness dynamic by overriding the lazy property at runtime:
<livewire:analytics-breakdown :lazy="! $user->prefersEagerLoad()" />
This is useful for power users who have opted into richer, faster dashboards.
Testing Lazy Components with Pest
Livewire's test helpers respect laziness. Use ->assertSeeHtml() on the placeholder, then call ->dispatch('livewire:load') to trigger hydration:
use Livewire\Livewire;
it('renders a placeholder before hydration', function () {
Livewire::test(RevenueChart::class)
->assertSeeHtml('animate-pulse');
});
it('renders chart data after hydration', function () {
Livewire::test(RevenueChart::class)
->call('$refresh')
->assertSee('Monthly Revenue');
});
Key Takeaways
- Add
lazyto any component whose data is expensive and not needed for the initial paint. - Always define a
placeholder()method to avoid layout shift and give users immediate feedback. - Props passed to lazy components are serialised and signed — treat them as public, not secret.
- Distinguish between lazy components (deferred boot) and deferred properties (deferred data within a booted component).
- Test placeholders and hydrated states separately; Livewire's test suite handles both cleanly.