livewire/livewire: Island views now inherit imports from their parent SFC
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:
IslandCompilernow extracts imports from the parent view. When a view containing@islandblocks is compiled, anyusestatements 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
usestatements out of the class portion of an SFC moved into a reusableParser::extractUseStatements()static method, so the parser and the island compiler share one implementation instead of drifting apart. - The extraction regex got smarter. It ignores
usestatements that appear inside comments and now excludes closureuse (...)clauses, so a function defined likefunction () use ($timestamp) { ... }in your view can't be mistaken for an import. - Regression tests were added covering PHP imports, Blade
@useimports, and a full SFC fixture that usesCarboninside 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@islanddirective. That matches where the SFC compiler hoists imports, and it keeps the extraction cheap and deterministic — your islands don't inherit randomusestatements 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 ordinaryusestatements 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:clearalong with your usual Livewire cache cleanup) so affected components recompile against the fixed compiler rather than serving stale, broken islands.
Takeaways
@islandblocks in SFCs now inherit the parent view's PHPusestatements — 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.