Livewire 4: Unified Single-File Components, 10x Faster Blade Rendering with Blaze, and Isolated Performance with Islands
Laravel
Summary:
Caleb Porzio unveils Livewire 4, focusing on unifying the component experience and significantly boosting performance. Key highlights include:
- Unified Component Creation: Livewire 4 defaults to single-file components (like Volt) with a new
⚡️.blade.php naming convention, simplifying component management.
- Enhanced PHP Integration: Leverages PHP 8.4's native getters and setters for cleaner property handling, including direct caching and validation.
- Improved User Experience: Introduces a
data-loading attribute for intuitive loading indicators and a robust slots/refs system for complex component interactions.
- Major Performance Gains:
- Smarter Polling: Prioritizes user actions over background polls, preventing UI freezes.
- Blaze Compiler: A new Blade compiler that uses "code folding" to render static HTML at compile time, making Blade components up to 10 times faster.
- Islands Architecture: Allows developers to isolate slow parts of a page (
@island directive) for independent rendering, lazy loading, and efficient infinite scrolling/pagination.
Introduction to Livewire 4 [0:00]
Caleb Porzio, the creator of Livewire and Alpine.js, announces Livewire 4. He emphasizes that it's not a complete rewrite and aims for mostly no breaking changes, allowing users to opt-in to new features.
Unified Component Experience [2:21]
Livewire 4 aims to unify component creation, addressing fragmentation caused by different methods like traditional Livewire and Volt.
- Single-File Components by Default:
- The
php artisan make:livewire <component-name> command now defaults to creating a single .blade.php file, similar to Volt's approach.
- This combines the PHP class and Blade view into one file, enhancing collocation.
- New file naming convention: Livewire components are denoted with a lightning bolt emoji (e.g.,
⚡️counter.blade.php) within the /resources/views/components/ directory. This visually distinguishes Livewire components from regular Blade components and improves sorting.
- Multi-File Components:
- An opt-out
--mfc (multi-file component) flag is available for those who prefer separate files.
- This creates a directory containing
counter.php (pure PHP class), counter.blade.php (Blade view), and counter.js (JavaScript).
- JavaScript files are now served as ES6 modules, allowing native ESX imports and better caching.
- New Project Structure Opinions:
- Dedicated
pages and layouts folders are introduced for better organization of routed pages and layouts.
- The
components directory becomes the default home for both Livewire and Blade components, promoting a consistent component lookup location.
- Enhanced JavaScript Integration:
- Removes the need for
@script directives. Naked <script> tags are supported at the bottom of the component file.
$wire is replaced by this.$, providing a more standard JavaScript syntax for interacting with Livewire properties and methods.
- Interceptors:
this.intercept() allows hooking into any Livewire action, enabling custom logic before/after requests (e.g., confirmation modals, scroll state management).
- Collocation of Tests:
- Tests can now be placed directly within the component directory (e.g.,
counter.test.php), aligning related code for easier development and testing.
- Integrates seamlessly with Pest 4 for headless browser testing (e.g.,
Livewire::visit('/counter')).
PHP 8.4 Property Accessors (Getters & Setters) [14:11]
Livewire 4 leverages PHP 8.4's native property accessors to streamline state management.
set Accessor:
- Allows direct inline validation or manipulation of a property when it's set.
- Example:
public int $count = 1 { set => max(1, $value); } prevents the counter from going below 1.
- This effectively replaces the need for
updating hooks for common scenarios.
get Accessor:
- Enables creating derived properties that are calculated on demand.
- Example:
protected int $multiple { get => $this->count * 5; }.
- These can replace traditional computed properties, providing a cleaner way to expose derived data to Blade views.
- Caching with Accessors:
- Getters and setters can be combined with Laravel helpers (e.g.,
cache()->forget(), cache()->put()) to implement property-level caching, ensuring data persistence across requests.
- Asymmetric Property Scope:
- PHP 8.4 allows defining different visibility for getting and setting properties (e.g.,
public get, protected set).
- This replaces Livewire's
#[Locked] attribute, preventing external mutation of a property while still allowing it to be read.
Enhanced Loading Indicators [17:54]
Livewire 4 improves the handling of loading states for a smoother user experience.
data-loading Attribute:
- Any HTML element that triggers a Livewire network request (e.g.,
wire:click) automatically receives a data-loading attribute during the request's duration.
- This allows for highly specific and declarative loading styles using CSS, especially with Tailwind CSS's data attribute selectors (e.g.,
data-loading:opacity-50).
- Targeted Loading States:
- Solves the common "double loading" problem where multiple elements might show a loading state unnecessarily.
- Now, loading indicators are strictly associated with the element that initiated the request, providing precise visual feedback.
- Supports
in-data-loading:<class> for child elements to react to their parent's loading state and not-in-data-loading:<class> for inverse behavior.
Slots, Attributes, and Refs [21:09]
Livewire components now fully support complex interaction patterns.
- Slots:
- Livewire components now officially support slots, similar to Blade components (
{{ $slot }}). This allows passing arbitrary HTML content into a component, making them more flexible and reusable.
- Attribute Forwarding:
- Component attributes can now be automatically forwarded to the root element of a Livewire component, simplifying styling and dynamic attribute application.
- Refs (
wire:ref):
- Introduces a ref system, allowing parent components to directly reference and interact with child Livewire components.
- Example:
wire:ref="modal" on a modal component, then this.$ref.modal.close() from the parent's PHP method.
- This eliminates the need for global event dispatches, preventing unintended side effects when multiple instances of a component are on a page.
Performance Overhauls [23:56]
Caleb addresses common performance concerns with Livewire by introducing significant under-the-hood optimizations.
- Smart Polling:
- The Livewire request system has been rewritten to prioritize human-initiated inputs over background polling requests.
- If a user clicks a button while a poll is active, the poll request is canceled, and the user's action is immediately processed, preventing UI unresponsiveness.
- Blaze Compiler [27:10]:
- A new compiler designed to dramatically speed up Blade rendering, especially for pages with many Blade components.
- Problem: Benchmarks showed that rendering many simple Blade components (
<x-card>) was significantly slower than direct PHP includes or simple echoes (e.g., 508ms for 25,000 components vs. 21ms for direct echo). This was attributed to Blade's compilation overhead, even with caching.
- Solution (Code Folding): Blaze sits in front of the Blade compiler. It identifies static parts of Blade templates (e.g., HTML tags, Tailwind classes) that don't change at runtime. These static parts are "code folded" (rendered at compile time) and then removed from the final PHP output.
- Result: Blade component rendering becomes up to 10 times faster, approaching the speed of pure PHP string concatenation. For the benchmark of 25,000 components, render time dropped to 19ms.
- Blaze benefits any Laravel application using Blade, not just Livewire.
- Islands Architecture [34:47]:
- Allows developers to isolate "expensive" (slow-rendering or data-fetching) parts of a Livewire component or Blade template into self-contained "islands".
- Usage: The
@island Blade directive wraps the section to be isolated (e.g., @island('revenue') ... @endisland).
- Benefit: When an action outside an island occurs, the island is not re-rendered, and its expensive operations are not re-executed, making interactions instant for the rest of the page. Conversely, actions within an island only re-render that specific island.
- Lazy Loading: Islands can be lazy-loaded (
lazy) with a placeholder (@placeholder) to make the initial page load instant, with the island's content loading asynchronously.
- Island-Specific Polling: Islands can have their own polling configurations (
poll="3s"), ensuring only that specific section refreshes without affecting other parts of the page.
- Advanced Rendering Modes (
mode):
mode="append": For infinite pagination scenarios, new results are appended to the existing content within the island, rather than replacing it.
mode="prepend": Similar to append, but new results are added to the top (e.g., for chat feeds).
wire:intersect: Integrates with Islands to enable true infinite pagination by triggering a load action when the bottom of an island comes into view.
Conclusion and Acknowledgements [41:56]
Caleb expresses excitement for Livewire 4's enhancements, believing it makes building fast and reactive applications even easier. He acknowledges contributors Josh Hanley (full-time Livewire developer) and Hugo (slide design for Flux UI).