spatie/mjml-sidecar
Compile MJML email templates to responsive HTML via AWS Lambda using Sidecar. A companion to spatie/mjml-php: deploy the provided MjmlFunction, then call Mjml::new()->sidecar()->toHtml($mjml) to render HTML without local Node/MJML.
Installation:
composer require spatie/mjml-sidecar
Requires spatie/mjml-php (installed automatically as a dependency).
First Use Case: Compile an MJML template directly in a Laravel controller or service:
use Spatie\MjmlSidecar\Facades\Mjml;
$html = Mjml::compile('
<mjml>
<mj-body>
<mj-section>
<mj-column>
<mj-text>Hello, World!</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
');
Where to Look First:
config/mjml.php (auto-generated) for configuration options.Template Compilation:
$html = Mjml::compile($mjmlString);
$html = Mjml::compileFile(resource_path('views/emails/welcome.mjml'));
$html = Mjml::compileView('emails.welcome', ['name' => 'John']);
Integration with Mailables:
use Spatie\MjmlSidecar\Facades\Mjml;
use Illuminate\Mail\Mailable;
class WelcomeMail extends Mailable {
public function build() {
return $this->subject('Welcome!')
->markdown('emails.welcome')
->withMjml(function ($mjml) {
$mjml->compileView('emails.welcome.mjml', ['name' => $this->name]);
});
}
}
(Note: Requires custom logic to replace the default HTML content.)
Caching Compiled Output:
$html = Mjml::compile($mjmlString, cache: true); // Caches for 1 hour by default
$html = Mjml::compile($mjmlString, cache: ['minutes' => 30]);
Error Handling:
try {
$html = Mjml::compile($mjmlString);
} catch (\Spatie\MjmlSidecar\Exceptions\MjmlException $e) {
report($e);
$html = '<p>Fallback HTML</p>'; // Graceful degradation
}
Dynamic MJML Generation: Use Blade templates to generate MJML dynamically:
$mjml = view('emails.dynamic_mjml', ['data' => $data])->render();
$html = Mjml::compile($mjml);
Sidecar Configuration:
Override Sidecar settings in config/mjml.php:
'sidecar' => [
'timeout' => 30, // seconds
'memory_limit' => '512M',
],
Testing: Mock the facade in tests:
$this->mock(Spatie\MjmlSidecar\Facades\Mjml::class)
->shouldReceive('compile')
->once()
->andReturn('<html>...</html>');
Sidecar Dependencies:
http://localhost:4444).
Configure via config/mjml.php:
'sidecar' => [
'url' => env('MJML_SIDECAR_URL', 'http://localhost:4444'),
],
MJML Syntax Errors:
Mjml::validate($mjmlString) to pre-check syntax:
if (!Mjml::validate($mjmlString)) {
throw new \InvalidArgumentException('Invalid MJML syntax');
}
Caching Quirks:
php artisan cache:clear
Memory Limits:
memory_limit in config/mjml.php or simplify templates.Facade vs. Direct Usage:
Spatie\MjmlSidecar\Mjml directly; use the facade for consistency.Enable Sidecar Logging:
Add to config/mjml.php:
'sidecar' => [
'debug' => env('APP_DEBUG', false),
],
Logs will appear in Laravel’s log channel.
Inspect Sidecar Requests:
Use a tool like Postman or curl to test Sidecar directly:
curl -X POST http://localhost:4444/compile -H "Content-Type: text/mjml" -d @template.mjml
Fallback to mjml-php:
If Sidecar fails, fall back to the original spatie/mjml-php:
use Spatie\Mjml\Mjml;
$html = Mjml::compile($mjmlString, [
'use_sidecar' => false,
]);
Custom Compilers: Extend the facade to add pre/post-processing:
Mjml::extend(function ($compiler) {
$compiler->afterCompile(function ($html) {
return str_replace('{app_name}', config('app.name'), $html);
});
});
Sidecar Middleware:
Add middleware to Sidecar requests (e.g., auth, rate limiting) by overriding the HTTP client in config/mjml.php:
'sidecar' => [
'client' => function () {
return new \GuzzleHttp\Client([
'timeout' => 30,
'headers' => [
'Authorization' => 'Bearer ' . env('SIDECAR_TOKEN'),
],
]);
},
],
Event Listeners: Listen for compilation events (e.g., log templates):
use Spatie\MjmlSidecar\Events\MjmlCompiled;
MjmlCompiled::listen(function ($event) {
\Log::debug('Compiled MJML for: ' . $event->template);
});
How can I help you explore Laravel packages today?