Skip to content
LaravelBlog

livewire/livewire: Island views now inherit imports from their parent SFC

LaravelBlogBot
LaravelBlogBot

Contributed by VernSG

Part of the livewire v4.4.7 release

Livewire PR #10338 fixes a subtle but persistent bug in the Single-File Component (SFC) toolchain: PHP use statements declared at the top of an SFC were never made available to the separate Blade views that @island blocks get compiled into. The parent view could resolve aliases like Carbon\Carbon just fine, but the moment you referenced the same alias inside an island, rendering failed with Class "Carbon" not found. This release carries the parent view's imports into every compiled island, so aliases now behave consistently across the whole file. If you're upgrading from v3.8.10, note that SFCs and islands are new in v4 — you'll never have known the broken behavior, but this fix means you can adopt islands without learning any import workarounds.

What changed

The fix touches two areas of the compiler, and both are invisible to your application code:

  • IslandCompiler now extracts imports from the parent view. When a view containing @island blocks is compiled, any use statements found in the leading <?php ... ?> blocks are captured once and prepended to each separately compiled island view. The same happens for Blade-level imports written as @use('Carbon\Carbon'), which are copied as-is so Blade can resolve them inside the island.
  • Use-statement extraction is now a shared parser helper. The logic that pulls use statements out of the class portion of an SFC moved into a reusable Parser::extractUseStatements() static method, so the parser and the island compiler share one implementation instead of drifting apart.
  • The extraction regex got smarter. It ignores use statements that appear inside comments and now excludes closure use (...) clauses, so a function defined like function () use ($timestamp) { ... } in your view can't be mistaken for an import.
  • Regression tests were added covering PHP imports, Blade @use imports, and a full SFC fixture that uses Carbon inside an island.

The bug, in practice

Islands exist so a piece of a page can be rendered, hydrated, and deferred independently of the rest of the component. To do that, Livewire extracts each @island block into its own compiled Blade file. The problem was that only the island's own markup made it into that file — the imports at the top of the parent view did not.

Consider an SFC like this:

<?php

use Carbon\Carbon;

new class extends Livewire\Component {
    public function with()
    {
        return ['timestamp' => Carbon::parse('2024-01-01')->timestamp];
    }
};
?>

<div>
    @island
        <span>year: {{ Carbon::createFromTimestamp($timestamp)->year }}</span>
    @endisland
</div>

The parent view compiled happily because it contained the use Carbon\Carbon; statement. But the extracted island view didn't, so Carbon::createFromTimestamp(...) inside the island threw:

Class "Carbon" not found

The failure also affected with: expressions, since those are evaluated in the island's scope too:

@island(name: 'timestamp', with: ['timestamp' => Carbon::parse('2024-01-01')->timestamp])
    <div>{{ Carbon::createFromTimestamp($timestamp)->year }}</div>
@endisland

With this patch, both the with: expression and the island body resolve Carbon correctly, because the compiled island now starts with the parent's imports.

Under the hood

Two implementation details are worth knowing, mostly so you can predict edge-case behavior:

  • IslandCompiler::extractImports() only looks at the content before the first @island directive. That matches where the SFC compiler hoists imports, and it keeps the extraction cheap and deterministic — your islands don't inherit random use statements buried mid-template.
  • Imports are injected above the island's generated scope-provider code, so they're in scope for everything the island evaluates, including the data passed via with:.

The shared Parser::extractUseStatements() helper uses a (*SKIP)(*FAIL) pattern to skip commented-out imports and a \buse\s+[^;()]+; pattern to avoid swallowing closure use clauses — a small but important correctness fix over the previous inline regex.

Upgrade impact

This is a pure bugfix with no breaking changes, no new directives, and no configuration. Three practical notes:

  • No code changes are required. Views that previously errored will simply start working after the recompile.
  • Workarounds can be removed. If you sidestepped the bug by fully-qualifying class names (for example, \Carbon\Carbon::parse(...) inside islands), you can switch back to ordinary use statements at the top of the SFC. Leaving the workaround in place is harmless, though.
  • Clear compiled caches when deploying. Compiled SFC and island views are cached on disk. After upgrading, run a cache clear during your deploy (for example php artisan view:clear along with your usual Livewire cache cleanup) so affected components recompile against the fixed compiler rather than serving stale, broken islands.

Takeaways

  • @island blocks in SFCs now inherit the parent view's PHP use statements — fixes issue #10278.
  • Blade @use(...) directives are also carried into compiled islands.
  • Import extraction is comment-safe and closure-safe via a shared Parser::extractUseStatements() helper.
  • No upgrade steps needed; just clear compiled view/Livewire caches after deploying so islands recompile.

Sources

More from this release

Related Articles