Skip to content
LaravelBlog

livewire/livewire: Ignore repeated lazy-load calls

LaravelBlogBot
LaravelBlogBot

Contributed by calebporzio

Part of the livewire v4.4.7 release

Livewire has hardened lazy loading against duplicate __lazyLoad requests. If you're coming from v3.8.10, you may have seen the report in discussion #10557: a lazy component that receives a second lazy-load request after it has already loaded would throw a MethodNotFoundException and pop the full-screen error dialog. This release ignores those repeat calls instead, while keeping genuine __lazyLoad misuse on non-lazy components properly rejected. There is no API change and no action required to upgrade.

What changed

Three coordinated edits inside the SupportLazyLoading feature drive the fix:

  • Hydration now recognizes a snapshot memo of lazyLoaded: true and records an internal isLazyLoadAlreadyLoaded flag on the component's per-request store, instead of only using the memo to skip re-mounting.
  • Dehydration now writes lazyLoaded: true back into the snapshot not just while a lazy load is actively resuming, but whenever the component is already loaded. This keeps the "loaded" state alive across every subsequent request.
  • The call hook intercepts __lazyLoad and, if the component has already loaded, claims the call and returns early — the method is never dispatched to the component at all.

The net effect: the first __lazyLoad resume behaves exactly as before, and every later __lazyLoad for that same loaded component becomes a harmless no-op.

Why it matters

Before this patch, the __lazyLoad interception only applied during the resume request. Once a lazy component had loaded, an incoming __lazyLoad call was no longer claimed by the feature, so Livewire treated it as an ordinary action and tried to dispatch __lazyLoad as a public method on the component — which doesn't exist. The result was a MethodNotFoundException and the full-screen error dialog for your users.

Repeat calls are easy to produce in the real world: a double-click, a retried request after a flaky connection, a stale snapshot replaying after the load already completed, or browser behavior firing the placeholder's load hook more than once. Any of these could turn a perfectly loaded component into a scary error screen.

As part of the fix, the test suite also confirms that mount() runs exactly once across consecutive replays — the component renders mounts:1 after three successive __lazyLoad calls, so heavy mount-side work (queries, SDK bootstrapping, external API calls) is never duplicated.

How the fix works

The subtle bug was state loss between requests. The lazyLoaded memo previously only survived on the request where the lazy load was actually resuming; on later requests nothing re-added it, so the call hook had nothing to check and let the method through. By persisting lazyLoaded: true in the dehydrated snapshot whenever the component is already loaded, and reading it back during hydration, the feature now has a durable "already loaded" signal that the call hook can use to short-circuit.

Importantly, security behavior is preserved. On a component that is not lazy, a __lazyLoad call remains unclaimed and still throws MethodNotFoundException — there's a new unit test asserting exactly this, so the fix can't be abused to probe method names or smuggle calls past component-security checks.

Upgrade impact

No code changes are needed. Lazy components written the usual way benefit automatically:

use Livewire\Attributes\Lazy;
use Livewire\Component;

#[Lazy]
class SalesReport extends Component
{
    public int $loads = 0;

    public function mount()
    {
        $this->loads++; // expensive setup — must run exactly once
    }

    public function placeholder()
    {
        return '<div>Loading report…</div>';
    }

    public function render()
    {
        return '<div>Report ready (mounts: '.$this->loads.')</div>';
    }
}

In v3.8.10, a replayed __lazyLoad('...') request against this component would throw MethodNotFoundException and show the error dialog. With this release, the first call loads the component, and any subsequent calls are silently ignored — the mount count stays at 1.

If you worked around the old bug — by catching exceptions around lazy loads, disabling lazy loading entirely, or adding your own request de-duplication — you can now remove those workarounds.

Verification

The fix is covered from multiple angles: the lazy-loading and component-security unit suites pass with 31 tests and 44 assertions, the existing focused lazy-loading browser test remains green (1 test, 2 assertions), and a real Laravel reproduction showed three consecutive replays ignored with the mount count staying at 1 and no exception dialog. The diff also passes git diff --check cleanly.

Takeaways

  • Repeated __lazyLoad calls on an already-loaded lazy component are now ignored instead of throwing MethodNotFoundException.
  • The lazyLoaded: true state persists across all subsequent snapshots, keeping mount a strict once-only guarantee.
  • __lazyLoad calls on non-lazy components still throw, so component-security behavior is unchanged.
  • Upgrading from v3.8.10 requires no code changes — and prior workarounds can safely be removed.

Sources

More from this release

Related Articles