Blazing Fast Blade: Optimizing Laravel Component Rendering with Caleb Porzio
Overview
Prerequisites
To get the most out of this tutorial, you should have a solid foundation in the following:
- Laravel Basics: Understanding the request lifecycle and service providers.
- Blade Components: Familiarity with anonymous components, props, and slots.
- PHP Performance Concepts: A basic understanding of how
opcacheworks and why file system lookups are expensive compared to in-memory operations. - Composer: Ability to manage packages via the PHPdependency manager.
Key Libraries & Tools
- Laravel Blaze: The core package that provides the optimized compiler and optimization directives.
- Livewire: While not strictly required, Blaze is built by theLivewireteam and integrates seamlessly with its reactive patterns.
- Flux: A UI component library that heavily utilizes Blaze to maintain high performance despite its complexTailwind CSSstructure.
- The Blaze Profiler: A built-in debugging tool that visualizes component render times and folding status.
Code Walkthrough: Implementing Blaze
Installation and Basic Setup
First, pull the package into your project using
composer require livewire/blaze
Once installed, you must opt-in your components to the Blaze compiler. You do this by adding the @blaze directive at the very top of your component file.
{{-- resources/views/components/button.blade.php --}}
@blaze
<button {{ $attributes }}>
{{ $slot }}
</button>
When you add @blaze, the package intercepts the standard app()->make() and view()->exists() calls at runtime, Blaze generates a plain
Level 2: Component Memoization
If your page renders the same component multiple times with the exact same attributes (like a status badge or a specific icon), you can enable memoization. This caches the rendered HTML in memory during a single request.
{{-- resources/views/components/status-pill.blade.php --}}
@blaze
@memo(true)
<span class="pill-{{ $type }}">
{{ $label }}
</span>
By adding @memo(true), Blaze creates a static cache key based on the component name and the serialized props. If you render this component 500 times with the same type and label,
Level 3: Code Folding and Partial Folding
The most aggressive optimization is Code Folding. This attempts to "pre-render" the component at compile time rather than runtime. If a component is entirely static, Blaze replaces the component call in your parent view with the actual HTML string during the compilation phase.
{{-- resources/views/components/icon.blade.php --}}
@blaze
@fold(true)
<svg ...> ... </svg>
When Blaze sees this icon in a parent view, it executes the
For components with dynamic parts, Blaze uses Partial Folding. It uses a tokenized parser to identify dynamic variables, replaces them with placeholders, renders the static shell, and then re-inserts the dynamic $label to a button.
Syntax Notes: The Tokenized Parser
Unlike standard
- Tokenization: It breaks the source code into a flat list of tokens (Tag Open, Tag Name, Attribute, String, Variable).
- AST Construction: It assembles these tokens into an Abstract Syntax Tree (AST). This tree understands that a
flux:buttoncontains aflux:iconas a child. - Transformation: Blaze traverses the AST. If it finds a component marked for folding, it executes the render logic.
- Code Generation: It spits out the final, optimized PHPfile.
This structured approach is what allows Blaze to "know" which parts of a component are safe to hardcode and which must remain dynamic.
Practical Examples: Boosting a Dashboard
Consider a dashboard with 1,000 table rows, each containing an avatar, a status badge, and an action dropdown.
- Without Blaze: Laravelperforms 3,000+ container lookups and merges thousands of attribute bags. This can easily take 150-200ms.
- With Blaze + Memoization: The avatar and status badge (often repeated) are memoized. The action dropdown is optimized into a function call. Total render time drops to ~15ms.
- With Blaze + Folding: The SVG icons within the dropdown are folded away. They no longer exist as PHPlogic at runtime. Total render time drops to <10ms.
Tips & Gotchas
- Static vs. Dynamic State: Code folding is a "sharp knife." If your component relies on global state (like
auth()->user()) but you don't pass that state as a prop, the component might fold based on the user who triggered the compilation. Always ensure folded components are pure functions of their props. - The Profiler: Use
BLAZE_DEBUG=truein your.env. This adds a floating button to your UI that breaks down exactly how many milliseconds each component took and why it was (or wasn't) folded. - The @unblaze Directive: If you have a specific block within a blazified component that must remain dynamic and escape the optimized compiler's logic, wrap it in
@unblaze. This is useful for validation errors or CSRF tokens that must be unique per render. - Anonymous Only: Currently, Blaze only optimizes anonymous Bladecomponents. Class-based components are not yet supported due to the complexity of their lifecycles and constructor logic.
