If you attended Laracon US this year, or if you're big into Livewire, you might already have heard about Blaze. Blaze is a new drop-in package (from the Livewire team, but it works on all Blade sites) that aims to dramatically optimize how Laravel renders Blade components. Think of it this way: even a simple <x-component /> tag carries runtime performance baggage, but Blaze can help us strip that baggage away.
Interested? The best way to learn how to use Blaze is to just check out the Blaze readme, which covers installation and customization.
Here, we're going to explore how Blaze works under the hood—by building two progressively capable toy versions of Blaze from scratch. We'll start simple and layer on complexity until we have something that mirrors the real Blaze. This will give you a solid mental model of what's happening every time Blaze touches one of your templates.
Let's get started! Install Blaze into your Laravel project by running:
composer require livewire/blaze
If you have a Livewire application using Flux, that's it! Blaze will automatically kick in and optimize your Flux components. Here's a before and after comparison on an application using Livewire, Flux, and Blaze; the performance overlays you see here are a tool that comes with Blaze:

⚠️ Caution: Please allow application to cool before handling ⚠️
Wow, that's fast!
But, remember, Blaze isn't locked into Livewire or Flux—you can use it in any Laravel project. You can run Blaze on individual components by adding the @blaze directive at the top of a Blade file:
@blaze
The rest of the component template goes here.
Or you can point Blaze at an entire directory from a service provider:
use Livewire\Blaze\Blaze;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Blaze::optimize()->in(resource_path('views/components'));
// ...
}
Blaze delivers its performance wins by shifting work from runtime to compile time. It offers three optimization strategies:
Even though the Function Compiler is the default, it's actually the newest addition to Blaze; compile-time folding is what started it all. It borrows a classic compiler design technique called constant folding, which we'll cover a bit more below!
To understand how these optimizations work under the hood, we'll build two basic clones from scratch: Smol Blaze will highlight compile time folding. Next, we'll take things a step further and build Lil Blaze, adding the full pipeline—including the Function Compiler (Blaze's default strategy)—to illustrate how Blaze is structured.
Now, we just threw out a bunch of big abstract words, so let's talk about what constant folding actually does.
In compiler optimization, constant folding reduces expressions like 3 + 5 to 8 during compilation, avoiding the need to compute them at runtime and saving the CPU.
Say you declare MAX_UPLOAD_SIZE like so:
// Source
const MAX_UPLOAD_SIZE = 10 * 1024 * 1024; // 10MB in bytes
A compiler with constant folding would evaluate the expression ahead of time and replace it with the result:
// After constant folding
const MAX_UPLOAD_SIZE = 10485760;
This is a trivial example, but hopefully you can see how unresolved expressions could quickly add up across a large codebase. Resolving these constants at compile time rapidly speeds things up.
Let's say you have <x-alert type="error" /> defined as:
@props(['type' => 'info'])
<div class="{{ $type }}">{{ $slot }}</div>
Then we use it like this:
<x-alert type="error">
I'm sorry dave, I'm afraid I can't do that.
</x-alert>
PHP, our beloved runtime language and savior, can't parse Blade out of the box, thus the Blade compiler needs to compile the Blade Component (excuse the reiteration) into something PHP can execute. Here's what our <x-alert type="error" ... looks like once compiled (don't worry about tracking exactly what's going on in here; just take a look at how it's shaped):
<?php $attributes ??= new \Illuminate\View\ComponentAttributeBag;
$__newAttributes = [];
$__propNames = \Illuminate\View\ComponentAttributeBag::extractPropNames((['type' => 'info']));
foreach ($attributes->all() as $__key => $__value) {
if (in_array($__key, $__propNames)) {
$$__key = $$__key ?? $__value;
} else {
$__newAttributes[$__key] = $__value;
}
}
$attributes = new \Illuminate\View\ComponentAttributeBag($__newAttributes);
unset($__propNames);
unset($__newAttributes);
foreach (array_filter((['type' => 'info']), 'is_string', ARRAY_FILTER_USE_KEY) as $__key => $__value) {
$$__key = $$__key ?? $__value;
}
$__defined_vars = get_defined_vars();
foreach ($attributes->all() as $__key => $__value) {
if (array_key_exists($__key, $__defined_vars)) unset($$__key);
}
unset($__defined_vars, $__key, $__value); ?>
<div class="<?php echo e($type); ?>"><?php echo e($slot); ?></div><?php /**PATH /resources/views/components/alert.blade.php ENDPATH**/ ?>
Now, any time we make use of the <x-alert /> component, the Blade view that uses it is transformed like this—once again, don't worry about fully understanding, just look at what all we have going on:
<?php if (isset($component)) { $__componentOriginal5194778a3a7b899dcee5619d0610f5cf = $component; } ?>
<?php if (isset($attributes)) { $__attributesOriginal5194778a3a7b899dcee5619d0610f5cf = $attributes; } ?>
<?php $component = Illuminate\View\AnonymousComponent::resolve(['view' => 'components.alert','data' => ['type' => 'error']] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?>
<?php $component->withName('alert'); ?>
<?php if ($component->shouldRender()): ?>
<?php $__env->startComponent($component->resolveView(), $component->data()); ?>
<?php if (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag): ?>
<?php $attributes = $attributes->except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?>
<?php endif; ?>
<?php $component->withAttributes(['type' => 'error']); ?>
I'm sorry dave, I'm afraid I can't do that.
<?php echo $__env->renderComponent(); ?>
<?php endif; ?>
<?php if (isset($__attributesOriginal5194778a3a7b899dcee5619d0610f5cf)): ?>
<?php $attributes = $__attributesOriginal5194778a3a7b899dcee5619d0610f5cf; ?>
<?php unset($__attributesOriginal5194778a3a7b899dcee5619d0610f5cf); ?>
<?php endif; ?>
<?php if (isset($__componentOriginal5194778a3a7b899dcee5619d0610f5cf)): ?>
<?php $component = $__componentOriginal5194778a3a7b899dcee5619d0610f5cf; ?>
<?php unset($__componentOriginal5194778a3a7b899dcee5619d0610f5cf); ?>
<?php endif; ?><?php /**PATH /resources/views/index.blade.php ENDPATH**/ ?>
That's a lot of overhead for a simple component! Imagine the bloat this is adding to a large codebase with many components. And that becomes further compounded when using component libraries.
We'll implement "Smol Blaze" to read a component's source at compile time and inline the resulting HTML directly into the template. This demo is deliberately minimal and only tackles folding, but it will help illustrate the moving pieces at play. We will keep all parts in the AppServiceProvider and, similar to Blaze, use regular expressions (although we'll take time to demystify the magic).
We will use Blade's prepareStringsForCompilationUsing() hook to allow us to intercept the raw template string before Blade compiles it:
// App/Providers/AppServiceProvider.php
public function register(): void
{
Blade::prepareStringsForCompilationUsing(function (string $template) {
return $this->foldComponents($template);
});
}
Let's scan the templates for self closing component tags (like <x-icon name="user" />) using a dash of regex. Check out the regex pattern:
protected function foldComponents(string $template): string
{
return preg_replace_callback(
'/<x-(\w+)\s+([^>]+?)\/>/i',
fn ($matches) => $this->foldComponent($matches[1], $matches[2]),
$template
);
}
Now for each match, let's locate the corresponding Blade file. If it doesn't exist, we'll opt out and return the original tag.
protected function foldComponent(string $name, string $attributes): string
{
$path = resource_path("views/components/{$name}.blade.php");
if (! file_exists($path)) {
return "<x-{$name} {$attributes} />";
}
$source = file_get_contents($path);
$source = preg_replace('/@\w+[^\n]*\n?/', '', $source);
return $this->replaceAttributes($source, $attributes);
}
Last, we need to parse the attributes string and replace the corresponding variables in the component source.
protected function replaceAttributes(string $html, string $attributes): string
{
preg_match_all('/(\w+)="([^"]*)"/', $attributes, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$key = $match[1];
$value = $match[2];
$html = str_replace('{{ $' . $key . ' }}', $value, $html);
}
return $html;
}
Your final AppServiceProvider should look like so:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
Blade::prepareStringsForCompilationUsing(function (string $template) {
return $this->foldComponents($template);
});
}
public function boot(): void {}
private function foldComponents(string $template): string
{
return preg_replace_callback(
'/<x-(\w+)\s+([^>]+?)\/>/i',
fn ($matches) => $this->foldComponent($matches[1], $matches[2]),
$template
);
}
private function foldComponent(string $name, string $attributes): string
{
$path = resource_path("views/components/{$name}.blade.php");
if (! file_exists($path)) {
return "<x-{$name} {$attributes} />";
}
$source = file_get_contents($path);
$source = preg_replace('/@\w+[^\n]*\n?/', '', $source);
return $this->replaceAttributes($source, $attributes);
}
private function replaceAttributes(string $html, string $attributes): string
{
preg_match_all('/(\w+)="([^"]*)"/', $attributes, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$key = $match[1];
$value = $match[2];
$html = str_replace('{{ $'.$key.' }}', $value, $html);
}
return $html;
}
}
With that, we have an overly-simplified constant folding compile-time Blade optimizer.
Smol Blaze only handles self-closing tags with static attributes, and can't deal with opening/closing tag pairs, dynamic attributes, slots, or nested components. Let's fix that! Lil Blaze's transformation journey also starts at Blade::prepareStringsForCompilationUsing, but the template passes through a pipeline of discrete stages:

Let's start by scaffolding the pipeline. The LilBlaze.php class will be the entry point, and each step will be a call to a private method on the class:
// app/LilBlaze/LilBlaze.php
<?php
namespace App\LilBlaze;
class LilBlaze
{
private array $folded = [];
public function compile(string $template): string
{
// Protect @php/@verbatim blocks
[$template, $blocks] = $this->store($template);
// Split into tokens
$tokens = $this->tokenize($template);
// Build AST from tokens
$nodes = $this->parse($tokens);
// Apply optimization
$this->walk($nodes);
// Render AST back to string
$output = implode('', array_map(fn ($n) => $n->render(), $nodes));
// Stamp folded dependencies
$output = $this->stamp($output);
// Restore protected blocks
return $this->restore($output, $blocks);
}
}
Before we tokenize, we need to protect blocks that might contain explicit code.
// app/LilBlaze/LilBlaze.php
private function store(string $template): array
{
$blocks = [];
$counter = 0;
$replace = function ($m) use (&$blocks, &$counter) {
$placeholder = "__LILBLAZE_BLOCK_{$counter}__";
$blocks[$placeholder] = $m[0];
$counter++;
return $placeholder;
};
$template = preg_replace_callback('/@verbatim(.*?)@endverbatim/s', $replace, $template);
// Same pattern for @php ... @endphp blocks
$template = preg_replace_callback('/@php(.*?)@endphp/s', $replace, $template);
return [$template, $blocks];
}
At the end of the pipeline, restore() puts the original blocks back:
private function restore(string $template, array $blocks): string
{
foreach ($blocks as $placeholder => $original) {
$template = str_replace($placeholder, $original, $template);
}
return $template;
}
Blaze does a similar(ish) thing, but through reflection calls Laravel's private BladeCompiler::storeUncompiledBlocks() method before tokenization.
In the tokenization process, we're basically converting the source code into typed chunks (each a Token), organized into a larger data structure (AST, or Abstract Syntax Tree).
Our pipeline will use three small classes to build tokens; first, the Token class:
// app/LilBlaze/Token.php
class Token
{
public function __construct(
// 'text', 'tag_open', 'tag_close', 'tag_self_close'
public readonly string $type,
// raw matched string
public readonly string $content,
// component name
public readonly string $name = '',
// attribute string (the 'type="error" class="bold"' in <x-alert type="error" class="bold">)
public readonly string $attributes = '',
) {}
}
Our parser will organize tokens into an AST made of two node types. A TextNode is a leaf, plain text or already-processed HTML:
// app/LilBlaze/TextNode.php
class TextNode
{
public function __construct(
public readonly string $content,
) {}
public function render(): string
{
return $this->content;
}
}
A ComponentNode is more complex; it can have children (text, nested components, slots) and knows how to render itself back into a Blade tag if no strategy handles it:
// app/LilBlaze/ComponentNode.php
class ComponentNode
{
public array $children;
public function __construct(
public readonly string $name,
public readonly string $attributes = '',
array $children = [],
public readonly bool $selfClosing = false,
) {
$this->children = $children;
}
public function render(): string
{
$attrs = $this->attributes ? " {$this->attributes}" : '';
if ($this->selfClosing) {
return "<x-{$this->name}{$attrs} />";
}
$children = implode('', array_map(fn ($n) => $n->render(), $this->children));
return "<x-{$this->name}{$attrs}>{$children}</x-{$this->name}>";
}
}
Smol Blaze used a single preg_replace_callback to find self closing tags. Lil Blaze splits the template into a flat array of typed Token objects, opening, closing, self closing, and text:
// app/LilBlaze/LilBlaze.php
private function tokenize(string $template): array
{
preg_match_all(
'/<\/?x-[\w\-:.]+[^>]*?\/?>/i',
$template,
$rawMatches,
PREG_OFFSET_CAPTURE
);
$result = collect($rawMatches[0])
->reduce(function ($carry, $match) use ($template) {
[$raw, $pos] = $match;
if ($pos > $carry['offset']) {
$carry['tokens'][] = new Token('text', substr($template, $carry['offset'], $pos - $carry['offset']));
}
$carry['tokens'][] = $this->classifyToken($raw);
$carry['offset'] = $pos + strlen($raw);
return $carry;
}, ['tokens' => [], 'offset' => 0]);
if ($result['offset'] < strlen($template)) {
$result['tokens'][] = new Token('text', substr($template, $result['offset']));
}
return $result['tokens'];
}
The classifyToken() method determines if a raw tag string is opening, closing, or self closing:
// app/LilBlaze/LilBlaze.php
private function classifyToken(string $raw): Token
{
if (str_starts_with($raw, '</')) {
preg_match('/<\/x-([\w\-:.]+)\s*>/', $raw, $matches);
return new Token('tag_close', $raw, $matches[1] ?? '');
}
if (str_ends_with(rtrim($raw, " \t>"), '/')) {
preg_match('/<x-([\w\-:.]+)\s*(.*?)\s*\/>/s', $raw, $matches);
return new Token('tag_self_close', $raw, $matches[1] ?? '', trim($matches[2] ?? ''));
}
preg_match('/<x-([\w\-:.]+)((?:\s[^>]*)?)>/s', $raw, $matches);
return new Token('tag_open', $raw, $matches[1] ?? '', trim($matches[2] ?? ''));
}
> inside attribute values (:class="$a > $b ? 'bg-red' : 'bg-blue'"), whereas our pure regex approach will fumble.Now we need to convert the flat array of tokens into a nested AST.
// app/LilBlaze/LilBlaze.php
private function parse(array $tokens): array
{
$stack = [];
return collect($tokens)
->reduce(function ($root, Token $token) use (&$stack) {
if ($token->type === 'tag_open') {
$node = new ComponentNode($token->name, $token->attributes);
$this->addChild($root, $stack, $node);
$stack[] = $node;
return $root;
}
match ($token->type) {
'text' => $this->addChild($root, $stack, new TextNode($token->content)),
'tag_self_close' => $this->addChild($root, $stack, new ComponentNode(
$token->name, $token->attributes, [], true,
)),
'tag_close' => array_pop($stack),
default => null,
};
return $root;
}, []);
}
private function addChild(array &$root, array &$stack, TextNode|ComponentNode $node): void
{
if (empty($stack)) {
$root[] = $node;
} else {
$stack[count($stack) - 1]->children[] = $node;
}
}
addChild places the node either at the root level or as a child of whatever ComponentNode is currently on top of the stack.
For example, this template:
<x-alert type="error">
I'm sorry dave, I'm afraid I can't do that.
</x-alert>
Produces this tree:
[
new ComponentNode(
name: 'alert',
attributes: 'type="error"',
children: [
new TextNode("\n I'm sorry dave, I'm afraid I can't do that.\n"),
],
),
]
We don't need to optimize any nodes of type TextNode, so we let them pass by.
For each ComponentNode, Lil Blaze will try two strategies in order. Folding only works when all attributes are static, so, if it's enabled, Lil Blaze will try that first, and if it fails, will fall back to the Function Compiler (compileNode). Even though folding is first, the Function Compiler will handle the vast majority of components.
// app/LilBlaze/LilBlaze.php
private function walk(array &$nodes): void
{
foreach ($nodes as $i => $node) {
if (! ($node instanceof ComponentNode)) {
continue;
}
if (! $node->selfClosing) {
$this->walk($node->children);
}
$result = $this->fold($node) ?? $this->compileNode($node);
if ($result !== null) {
$nodes[$i] = $result;
}
}
}
When a strategy succeeds, it converts the ComponentNode into a TextNode. If it returns null, the next strategy gets a shot.
@blaze(fold: true) or per directory via Blaze::optimize(). When folding is enabled but can't proceed (e.g. when dynamic props are detected), it falls back to function compilation. Memoization sits alongside these as a runtime caching layer for components that appear repeatedly with the same props. We skip memoize in Lil Blaze since fold and compile are purely compile time transformations.Smol Blaze demonstrated this optimization, but now it's gated behind a safety check: if any attribute is dynamic (:class="$expr"), folding is skipped.
// app/LilBlaze/LilBlaze.php
private function fold(ComponentNode $node): ?TextNode
{
$path = resource_path("views/components/{$node->name}.blade.php");
if (! file_exists($path)) {
return null;
}
// Skip dynamic components
if (preg_match('/:\w+\s*=/', $node->attributes)) {
return null;
}
preg_match_all('/(\w+)="([^"]*)"/', $node->attributes, $matches, PREG_SET_ORDER);
$source = file_get_contents($path);
$source = preg_replace('/@\w+[^\n]*\n?/', '', $source);
// Replace attributes
foreach ($matches as $match) {
$source = str_replace('{{ $'.$match[1].' }}', $match[2], $source);
}
// Replace default slot from children
if (! $node->selfClosing) {
$slotContent = implode('', array_map(fn ($n) => $n->render(), $node->children));
$source = str_replace('{{ $slot }}', $slotContent, $source);
}
$source = preg_replace('/\{\{\s*\$\w+\s*\}\}/', '', $source);
$this->recordFolded($node->name, $path);
return new TextNode($source);
}
Folder replaces dynamic attributes with BLAZE_PLACEHOLDER_* strings, does an isolated Blade render at compile time, then restores the placeholders with the original expressions. It also checks for "problematic patterns" like stateful references ($errors, @csrf, session()) that can't be evaluated at compile time.In scenarios where we can't fold, we'll swap out Laravel's heavy function calls (the ComponentAttributeBag, extractPropNames, get_defined_vars dance we saw earlier) with a simple require_once + function call. Before we look at the code, here's the end result we're aiming for. Given this template:
<x-alert :type="$type">
I'm sorry dave, I'm afraid I can't do that.
</x-alert>
The compile strategy produces a standalone PHP function for the component:
<?php if (!function_exists('_lilblaze_a1b2c3d4e5f6')):
function _lilblaze_a1b2c3d4e5f6(array $__data = []) {
$__data = array_merge(['type' => 'info'], $__data);
extract($__data);
ob_start();
?>
<div class="<?php echo e($type); ?>"><?php echo e($slot); ?></div>
<?php
echo ltrim(ob_get_clean());
}
endif; ?>
And the call site in the compiled view becomes:
<?php ob_start(); ?>
I'm sorry dave, I'm afraid I can't do that.
<?php $__slot = ob_get_clean();
require_once storage_path('framework/views/_lilblaze_a1b2c3d4e5f6.php');
_lilblaze_a1b2c3d4e5f6(array_merge(['type' => $type], ['slot' => $__slot])); ?>
Compare that to the 20+ lines of ComponentAttributeBag boilerplate we saw earlier. Much leaner, right? Now let's look at the three methods that make this happen.
compileNode is the entry point. It generates a unique function name from the component path, delegates to wrapComponent to produce the function file, and returns a TextNode with either a simple function call (self-closing) or an ob_start/ob_get_clean wrapper to capture slot content:
// app/LilBlaze/LilBlaze.php
private function compileNode(ComponentNode $node): ?TextNode
{
$path = resource_path("views/components/{$node->name}.blade.php");
if (! file_exists($path)) {
return null;
}
$hash = substr(md5($path), 0, 12);
$funcName = "_lilblaze_{$hash}";
$this->wrapComponent($path, $funcName);
$attrs = $this->buildAttributeArray($node->attributes);
$requirePath = "storage_path('framework/views/{$funcName}.php')";
if ($node->selfClosing) {
return new TextNode(
"<"."?php require_once {$requirePath}; {$funcName}({$attrs}); ?>"
);
}
$slotContent = implode('', array_map(fn ($n) => $n->render(), $node->children));
return new TextNode(
"<"."?php ob_start(); ?>{$slotContent}<"."?php \$__slot = ob_get_clean(); "
."require_once {$requirePath}; "
."{$funcName}(array_merge({$attrs}, ['slot' => \$__slot])); ?>"
);
}
Note: Please ignore the weird syntax around the opening PHP braces, it's just to trick our blog's syntax highlighter temporarily.
wrapComponent does the heavy lifting. It reads the component's Blade source and transforms it into a plain PHP function: extract @props defaults, strip all Blade directives, convert {{ }} mustaches into <{{'?php'}} echo e(...) ?>, then wrap everything in a named function that merges defaults with the passed data:
// app/LilBlaze/LilBlaze.php
private function wrapComponent(string $sourcePath, string $funcName): void
{
$compiledPath = storage_path("framework/views/{$funcName}.php");
if (file_exists($compiledPath) && filemtime($compiledPath) >= filemtime($sourcePath)) {
return;
}
$source = file_get_contents($sourcePath);
$defaults = '[]';
if (preg_match('/@props\(\s*(\[.*?\])\s*\)/s', $source, $m)) {
$defaults = $m[1];
}
$source = preg_replace('/@\w+\(.*?\)\s*\n?/s', '', $source);
$source = preg_replace('/@\w+\s*\n?/', '', $source);
$source = preg_replace_callback(
'/\{\{\s*(.+?)\s*\}\}/s',
fn ($m) => '<'.'?php echo e('.$m[1].'); '.'?>',
$source
);
$php = implode(PHP_EOL, [
"<"."?php if (!function_exists('{$funcName}')):",
"function {$funcName}(array \$__data = []) {",
" \$__data = array_merge({$defaults}, \$__data);",
' extract($__data);',
' ob_start();',
"?>{$source}<"."?php",
' echo ltrim(ob_get_clean());',
'}',
'endif; ?>',
'',
]);
file_put_contents($compiledPath, $php);
}
Finally, buildAttributeArray converts the raw attribute string into a PHP array literal, handling both static (key="value") and dynamic (:key="$expr") attributes:
// app/LilBlaze/LilBlaze.php
private function buildAttributeArray(string $attributes): string
{
if (empty(trim($attributes))) {
return '[]';
}
preg_match_all('/:?(\w+)="([^"]*)"/', $attributes, $matches, PREG_SET_ORDER);
if (empty($matches)) {
return '[]';
}
$pairs = array_map(function ($matching) {
$isDynamic = str_starts_with($matching[0], ':');
return $isDynamic
? "'".addslashes($matching[1])."' => ".$matching[2]
: "'".addslashes($matching[1])."' => '".addslashes($matching[2])."'";
}, $matches);
return '['.implode(', ', $pairs).']';
}
Wrapper is more comprehensive. It handles @aware, hoists use statements above the function, and runs the template through the full Blade compiler.After the walker has processed all nodes, any components that were folded need to be tracked. If the component source file changes later, the cached compiled view becomes stale. So, we need to prepend dependency markers to the output, and track them as the walker runs:
// app/LilBlaze/LilBlaze.php
private array $folded = [];
private function recordFolded(string $name, string $path): void
{
$this->folded[] = [
'name' => $name,
'path' => $path,
'mtime' => filemtime($path),
];
}
private function stamp(string $compiled): string
{
$header = '';
foreach ($this->folded as $dep) {
$header .= "<"."?php /* [LilBlazeFolded]:{$dep['name']}:{$dep['path']}:{$dep['mtime']} */ ?>".PHP_EOL;
}
$this->folded = [];
return $header . $compiled;
}
Blaze hooks into Laravel's composing:* view events to automatically detect stale folded dependencies and trigger recompilation.
The AppServiceProvider wires it up the same way as Smol Blaze:
// app/Providers/AppServiceProvider.php
public function register(): void
{
$this->app->singleton(LilBlaze::class);
Blade::prepareStringsForCompilationUsing(function (string $template) {
return app(LilBlaze::class)->compile($template);
});
}
Also here is the full example of Lil Blaze in action.
Hopefully, building Smol Blaze and Lil Blaze gave you a solid mental model of what Blaze does under the hood. For most apps, just installing the package and letting the Function Compiler do its thing will be enough. When you need more, folding and memoization are there.
The full Blaze pipeline is significantly more involved than what we built here. The Livewire team (especially Filip) has been hard at work making it as bulletproof and performant as possible. Perhaps someday these wins will be ported back to the mothership's (Laravel) core Blade compiler engine, but as of now I'll be installing it on every Blade-heavy Laravel app to gain some of these sweet perf wins.
Have you tried Blaze on your projects? Let us know how it's going. Until next time!
We appreciate your interest.
We will get right back to you.