Livewire 3 Lazy Components: Defer the Expensive Stuff, Not Everything
Livewire 3's #[Lazy] attribute defers expensive component rendering to a follow-up request. Here's how it works, when it helps, and why lazy-loading everything backfires.
25 Jul 2026, 21:37 UTC

Your dashboard page takes 900ms to respond, and the profiler shows that most of that time belongs to a revenue chart that sits below the fold. The user stares at a blank browser tab waiting for data they will not even see until they scroll. Livewire 3's lazy components exist for exactly this situation: skip the expensive render on the initial request, send a lightweight placeholder, and let the component load itself in a follow-up request.
The takeaway up front: lazy loading is a targeted fix for measured render cost, not a default setting. Used well, it cuts time-to-first-byte dramatically. Used indiscriminately, it turns one slow request into a burst of slow requests and can make things worse.
How lazy loading works in Livewire 3
When you mark a component as lazy, Livewire skips its normal mount-and-render cycle during the initial page load. Instead, it embeds a small placeholder in the HTML. Once the page reaches the browser, Livewire automatically issues a second request to its update endpoint, and that follow-up request runs the full component lifecycle — mount, render, the expensive query, all of it — and swaps the real HTML into place.
There are two ways to opt in. Inline, at the include site:
<livewire:revenue-chart lazy />Or on the component class itself, which makes every usage lazy:
use Livewire\Attributes\Lazy;
#[Lazy]
class RevenueChart extends Component
{
// ...
}These examples assume Livewire 3. Livewire 2 used different patterns, so check your composer.json before copying anything — you can also confirm the #[Lazy] attribute exists in vendor/livewire/livewire/src/Attributes.
A worked example with a placeholder
Here is a component with a deliberately heavy query, plus a skeleton placeholder so the layout does not jump when the real content arrives:
namespace App\Livewire;
use Livewire\Component;
use Livewire\Attributes\Lazy;
#[Lazy]
class RevenueChart extends Component
{
public function render()
{
return view('livewire.revenue-chart', [
'totals' => \App\Models\Order::query()
->selectRaw('DATE(created_at) as day, SUM(total) as revenue')
->where('created_at', '>=', now()->subDays(90))
->groupBy('day')
->get(),
]);
}
public function placeholder()
{
return view('livewire.placeholders.revenue-chart');
}
}The placeholder() method renders while the follow-up request is in flight. A simple skeleton div with a fixed height works well — matching the approximate height of the final chart avoids layout shift, which matters for Core Web Vitals if this page is public-facing.
To verify the behavior, open your browser's network tab and load the page. You should see the initial document return quickly with the placeholder markup, followed by a request to Livewire's update endpoint that carries the real component payload. Compare the initial response time against the same page with the lazy flag removed — that difference is your actual win, and it is worth measuring rather than assuming.
The trade-off: extra round trips and unchanged server work
Lazy loading defers work; it does not delete it. The follow-up request still runs the full mount and render path, so total server cost per page view is the same or slightly higher (you are paying for an extra request's overhead). What improves is when the user sees something useful.
The sharper edge is isolation. By default, each lazy component fires its own follow-up request. Put six lazy widgets on one dashboard and you get a burst of six simultaneous requests hitting your database at once — potentially slower overall than one patient initial render. Livewire 3 offers an escape hatch:
#[Lazy(isolate: false)]With isolate: false, multiple lazy components on the same page are batched into a single update request. Use it for widgets that should appear together, like a row of stat cards.
What not to lazy-load
Two categories should stay eager. First, anything SEO-relevant: placeholder content is what crawlers see in the initial HTML, so a lazy-loaded product description or article body effectively disappears from the page's indexable content. Second, anything the user must interact with immediately — a primary form, a checkout summary. Making the most important element on the page wait for a second request is the opposite of the optimization you wanted.
Lazy loading also composes fine with other Livewire 3 performance features like computed properties (cached within a request) and wire:navigate, but none of these substitute for fixing a genuinely bad query. If the chart query itself is slow, an index will help more than a placeholder.
A practical rule
Profile first. Find the components that dominate your page's render time, check whether they sit below the fold or outside the critical interaction path, and lazy-load those specifically. Give each one a sized placeholder, batch related widgets with isolate: false, and re-measure in the network tab. If the initial response did not get meaningfully faster, take the attribute back out — the extra round trip was not paying for itself.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.