livewire/blaze
Blaze is a drop-in replacement for anonymous Blade components, compiling templates into optimized PHP functions and eliminating 91–97% of rendering overhead. Optional memoization and folding deliver even faster repeated renders.
Blaze is a drop-in compiler for anonymous Blade components. Day one, it is a one-line change: install it, point it at a component directory, clear compiled views, and measure. There is no rewrite, no new syntax to learn, and no runtime API to adopt — your templates stay Blade.
Install and first use case. Try it on a single component first rather than switching everything on:
composer require livewire/blaze:^1.0
{{-- resources/views/components/button.blade.php --}}
@blaze
<button {{ $attributes }}>
{{ $slot }}
</button>
php artisan view:clear
That is the whole loop. If you're on Flux UI, the README states Blaze picks up Flux components automatically once installed — nothing to configure, which is the fastest way to feel the performance delta on a real page.
Where to look first. Three files tell you almost everything about the blast radius:
resources/views/components/ — the component tree you'd optimize, and its directory shape (which tells you how to slice Blaze::optimize() calls).app/Providers/AppServiceProvider.php — where Blaze::optimize() lives; check whether it's already configured before adding anything.composer.json — presence of livewire/flux, livewire/livewire, and existing livewire/blaze.Also grep providers for anonymousComponentPath and anonymousComponentNamespace. Apps commonly register extra component roots outside resources/views/components, and those directories need their own ->in(...) entry or they silently stay uncompiled.
The one thing to internalize before enabling broadly. Blaze's default strategy is the function compiler, and it is the safe one — the README claims 91–97% pipeline overhead reduction with identical HTML output. The two advanced strategies (memo, fold) change semantics, not just speed. Start with compilation only, then graduate.
Note also that this is a young 1.x package with a small dependent base. Treat it as you would any new compiler in your build: enable incrementally, keep the rollout reversible by deleting one provider call plus view:clear, and verify rendered markup on your heaviest pages.
Directive for trialling, directories for rollout. The @blaze directive is for experiments and one-offs; Blaze::optimize()->in(...) is the production shape. The README explicitly recommends starting with specific directories and expanding gradually, because your app may depend on unsupported features.
use Livewire\Blaze\Blaze;
public function boot(): void
{
Blaze::optimize()
->in(resource_path('views/components/button'))
->in(resource_path('views/components/modal'));
}
Layer strategies by directory, not by component. The per-folder configuration is the real day-to-day lever, because different component families have genuinely different risk profiles:
Blaze::optimize()
->in(resource_path('views/components/icons'), memo: true) // repeated, no slots
->in(resource_path('views/components/cards'), fold: true) // static output
->in(resource_path('views/components'))
->in(resource_path('views/components/legacy'), compile: false);
Component-level @blaze(...) always overrides directory-level settings, so you can opt a single file out of a folder-wide strategy without restructuring. Exclusions accept a directory or a single file — useful when one component in a folder uses auth() or $errors.
Do a compatibility pass before each expansion. The cheapest reliable gate is a grep sweep of the directory you're about to add:
| Pattern | Grep for | Why it breaks |
|---|---|---|
| Class-based components | PHP classes in app/View/Components/ |
Not supported at all |
$component variable |
$component |
Unavailable |
| View composers / creators | View::composer(, View::creator( |
Never fire |
| Shared data | View::share( then the variable names |
Not auto-injected |
view() rendering |
view('components., View::make('components. |
Only component tags work |
Anything flagged gets a compile: false and a note, not a rewrite on day one.
Use debug traces to target memo and fold. Set BLAZE_DEBUG=true, browse the slow pages, and let the trace data tell you which components are worth memoizing or folding. This is materially better than guessing — the alternative (static code analysis) is speculative and can break components, per the skill guidance. Then remove the flag.
Integration tips.
@aware propagation only works when both parent and child are Blaze-compiled. If a parent stays excluded, children silently see empty @aware values — a bug that looks like Blaze is broken when it's a configuration split.View::share('x', ...) reads with $__env->shared('x') in the component. That's the sanctioned migration and it keeps the component compilable.@props declarations as the boundary marker: props are "internal logic" inputs, everything else is pass-through. That mental model predicts almost every folding behaviour.view:clear.view:clear is not optional. Every configuration change — adding a directory, changing a strategy, removing a component from the list — needs php artisan view:clear. Stale compiled functions are the single most common "Blaze is doing something weird" symptom.
Folding is the sharp edge. Function compilation is near-zero-risk; folding removes the component from runtime entirely and bakes static HTML at compile time. Everything below is a folding-specific footgun:
User::get(), auth()->check(), @auth, session(), request()->is(), $errors->has(), now(), @csrf — all produce wrong output because they're evaluated once, at compile time, in a different context. Blaze attempts to detect this and throws, but cannot catch everything.@props key abort folding automatically. That's defensive and correct: Blaze substitutes a placeholder like ATTR_PLACEHOLDER_1, $color becomes the literal string, a match falls through to default, and your button is silently always gray. If you see "why is this one prop stuck at its default", this is why.:id="Str::random()" inside a folded component works correctly.@props quietly downgraded the component to function compilation.Memoization only applies to components without slots. Icons and avatars are the canonical fit. Because the cache key is component name + props, be careful with props that are objects or non-deterministic — the key is derived from props, so two logically different renders with equal-looking props will share an entry.
Unsupported features, in checklist form. Class-based components; the $component variable; view composers/creators/lifecycle events; auto-injecting View::share() data; cross-boundary @aware (mixed Blaze/non-Blaze); and rendering via view() — components are tag-only (<x-...>). Each of these fails quietly rather than loudly, which is why the compatibility grep matters more than reading the limitation list once.
Class-based components are worth converting. In most apps the class is a thin prop default or a computed value, and moving it into @props + @php brings the component under compilation. That's usually the highest-leverage refactor when the exclusion list is long.
Exclude, don't delete, when something misbehaves. ->in($path, compile: false) on a directory or a single file is a one-line rollback that keeps the rest of the folder optimized. Use it while you diagnose a misrender instead of unwinding the whole rollout.
Debug mode is a temporary state. BLAZE_DEBUG=true collects trace data; leaving it on in production is dead weight. Flip it on for a profiling session, act on the traces, flip it off.
How can I help you explore Laravel packages today?