Skip to content
LaravelBlog

livewire/livewire: Share the component instance with views during an island render

LaravelBlogBot
LaravelBlogBot

Contributed by patrickweh

Part of the livewire v4.4.7 release

Livewire v4 ships a targeted fix for the islands feature: when an island was rendered on its own through renderIsland(), Blade components inside it lost their Livewire context. The most visible symptom was a custom Blade input with a wire:model binding coming back empty after an island re-render — the server still held the value, but the freshly rendered markup arrived unbound. The root cause was an asymmetry between the full component render path and the island render path in how the component instance is exposed to views. PR #10612 makes both paths consistent by sharing __livewire with views for the duration of an island render, then restoring the previous state. For anyone coming from v3.8.10, this is a behavior fix with no API changes — it matters as soon as you adopt islands in v4 and nest Blade components inside them.

The bug: bindings disappear inside standalone island renders

Islands let you re-render a small fragment of a component in isolation. That works well until a third-party Blade component is involved:

@island(name: 'filters')
    <x-inputs.search wire:model="query" />
@endisland

On the initial full render, everything is fine. But when that island is re-rendered on its own — for example via $this->renderIsland('filters') — the package behind x-inputs.search could no longer tell it was running inside Livewire, so it dropped its wire:model handling. The input rendered as a plain, empty control until the next full page load put the binding back.

What changed under the hood

The two render paths exposed the component instance differently. A full render shares the instance with every view involved:

$revertA = Utils::shareWithViews('__livewire', $component);

The island path, however, only placed it in the view's own data:

$scope = array_merge(['__livewire' => $this], $properties);
$view->with($scope);

That distinction matters because Blade components render in their own view instance. Data passed with with() never reaches them, while shared view data does. Any package that checks View::shared('__livewire') to detect the Livewire context therefore saw an empty value during a standalone island render and concluded it was outside Livewire.

The fix, in HandlesIslands::renderIslandView(), mirrors the full render behavior:

$revertShare = Utils::shareWithViews('__livewire', $this);

$finish = trigger('renderIsland', $this, $name, $view, $properties);

$html = $view->render();

$revertShare();

shareWithViews() remembers the previous value and restores it afterwards, so nested islands and nested component renders keep the correct instance at every level. This is also the same pattern SupportValidation::renderIsland() already used a few lines away for the error bag, so the island code path is now internally consistent.

Reproducing it in a test

The PR includes a unit test that fails without the change and passes with it:

Livewire::test(new class extends Component {
    public function repaint()
    {
        $this->renderIsland('probe');
    }

    public function render()
    {
        return <<<'HTML'
        <div>
            @island(name: 'probe')
                <div>shared: {{ app('view')->shared('__livewire') ? 'yes' : 'no' }}</div>
            @endisland
        </div>
        HTML;
    }
});

The initial render prints shared: yes. Before the fix, calling repaint() produced an island fragment printing shared: no — proof that the shared context vanished during the standalone render. After the fix, the fragment prints shared: yes too, and the rest of the island unit tests pass unchanged, confirming no regressions.

What this means for your upgrade

If you are moving from v3.8.10 to v4, there is nothing to migrate here — islands are new in v4, and this fix simply makes them behave correctly from the start. Specifically:

  • If you hit this bug in earlier v4 builds, inputs bound through custom Blade components no longer go blank after an island re-render. You can remove any workarounds, such as avoiding Blade components inside @island blocks or manually re-attaching bindings after a repaint.
  • If you author Livewire-aware packages, context detection via View::shared('__livewire') is now reliable in both render paths. If your package branches on that check to decide whether to apply wire:model handling, inject assets, or scope styles, island fragments will now behave exactly like the rest of the page.
  • One behavioral note: views rendered during an island render can now see __livewire in shared data. The share is reverted immediately after render(), and the previous value is restored, so nothing leaks between requests or renders — but if a package snapshots shared view data mid-render, it will now observe the component instance where it previously saw nothing.

Takeaways

  • Island renders now share the component instance with views, matching the full render path; Blade components and packages detect Livewire correctly inside islands.
  • The concrete bug fixed: wire:model bindings on custom Blade components vanished when their island was re-rendered via renderIsland().
  • Utils::shareWithViews() restores the prior value afterwards, keeping nested renders correct.
  • No public API changes and no upgrade steps — just remove workarounds if you had them, and expect View::shared('__livewire') to be trustworthy during island renders.

Sources

More from this release

Related Articles