illarra/email-bundle
Illarra EmailBundle is a Symfony/PHP package that streamlines sending emails from applications. It offers configurable mail transport integration, templated message composition, and convenient services for delivering transactional and notification emails.
Start with the go/no-go check, because for a Laravel developer the honest answer is that you cannot leverage this bundle day-to-day. It is a Symfony 2.x bundle, not a Laravel package: it registers through AppKernel::registerBundles(), resolves config through a Symfony Extension/Configuration tree, and renders with Twig. There is no ServiceProvider, no facade, no Blade namespace, and no package auto-discovery. composer require illarra/email-bundle on a modern Laravel project will either fail dependency resolution (Symfony ~2.3–~2.8, PHP 5.x/7.0-era constraints) or "succeed" only with --ignore-platform-reqs and a broken autoload graph. Don't force it.
The only scenario where you touch the code directly is maintaining an existing Symfony 2.7/2.8 app that already depends on it. In that case:
composer require illarra/email-bundle
// app/AppKernel.php
public function registerBundles()
{
$bundles = [
// ...
new Illarra\EmailBundle\IllarraEmailBundle(),
];
}
# app/config/config.yml
illarra_email:
default:
from: { email: [email protected], name: "Acme" }
reply_to: [email protected]
theme:
path: "%kernel.root_dir%/Resources/views/emails"
layout: layout.html.twig
Where to look first (if reading it): DependencyInjection/Configuration.php is the authoritative list of options — docs are sparse — then Resources/config/services.xml for service IDs you can decorate or override. The public surface is small: a mailer service plus a Twig-rendered message object. Ten minutes of reading covers it.
First use case for a Laravel team: don't send an email with it — read it once, steal the design, and close the ticket. Then build the Laravel equivalent you actually need:
php artisan make:mail WelcomeMail --markdown=mail.welcome
php artisan vendor:publish --tag=laravel-mail # theme in resources/views/vendor/mail
// app/Mail/WelcomeMail.php
class WelcomeMail extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;
public function __construct(public User $user) {}
public function build(): self
{
return $this->theme('acme')
->subject('Welcome to Acme')
->markdown('mail.welcome', ['user' => $this->user]);
}
}
Mail::to($user->email)->queue(new WelcomeMail($user));
That two-line controller call is the "service-based mailer" the bundle promised — Laravel already ships it.
The bundle's value is conceptual, not executable in Laravel. Three of its ideas port cleanly:
1. Config-driven theming resolved in a base layout. The bundle's best idea is a theme.path + layout pair that every message inherits. Reproduce it in Blade rather than Twig:
// config/mail-theme.php
return [
'default' => 'acme',
'themes' => [
'acme' => [
'layout' => 'mail.layouts.acme',
'from' => ['address' => '[email protected]', 'name' => 'Acme'],
'reply_to' => '[email protected]',
],
],
];
{{-- resources/views/components/mail/layout.blade.php --}}
@props(['theme' => config('mail-theme.default')])
<x-dynamic-component :component="'mail.'.$theme.'.wrapper" {{ $attributes }}>
<x-mail.button href="...">Get started</x-mail.button>
</x-dynamic-component>
2. Template-driven messages with shared chrome. The bundle rendered content through a layout in Twig. In Laravel, factor the chrome into components (<x-mail.layout>, <x-mail.button>, <x-mail.panel>) and keep Markdown mailables for the body. Publish resources/views/vendor/mail and treat it as the single source of brand truth — one file to change when the design system moves.
3. CSS inlining as a pipeline step. This is the actually hard part, and the bundle's README glosses over it. Wire it into a base mailable so it never leaks into individual mailables:
abstract class ThemedMailable extends Mailable
{
public function build(): self
{
return $this->withSymfonyMessage(function (Email $message) {
$message->html((new CssInliner())
->inlineHtml($message->getHtmlBody()));
});
}
}
tijsverkoyen/css-to-inline-styles is already a transitive Laravel dependency — you don't add vendor weight.
4. Testability via swapping. The bundle advertises a "swappable mailer." Laravel's Mail::fake() is strictly better:
Mail::fake();
$this->post('/register', [...]);
Mail::assertQueued(WelcomeMail::class, fn ($m) => $m->hasTo('[email protected]'));
Add a rendered-HTML snapshot per mailable for regressions:
$html = (new WelcomeMail($user))->render();
$this->assertStringContainsString('class="btn-primary"', $html);
$this->assertMatchesSnapshot($html);
Workflow tips: queue every transactional mail (ShouldQueue + a Mail::to()->queue() call) so delivery latency never blocks a request; put the preview route behind app()->isLocal() and render each mailable to HTML for visual review — that replaces the bundle's Symfony profiler integration; and add a CI check that fails on un-inlined <style> blocks or missing plaintext alternatives.
ServiceProvider, no auto-discovery (the composer.json type is symfony-bundle, so Laravel's discovery silently skips it), no Blade integration. Any roadmap line assuming drop-in usage should be closed now.--ignore-platform-reqs. It "installs" but produces an autoload graph that will fail on the next PHP or Laravel upgrade and block unrelated work. That failure is silent until someone needs to deploy.<style> blocks. Inline everything, but keep a small <style> for responsive media queries — clients that strip styles will fall back to the inlined desktop layout.<!--[if mso]>), VML buttons, and dark-mode overrides via prefers-color-scheme are hand-written per template. The bundle gives you none of this; budget it explicitly rather than assuming "we integrated the email bundle" solved it.%kernel.root_dir%/Resources/views/emails. In Laravel use resource_path('views/mail'); don't copy the YAML — rewrite the theme config natively.from: { email, name } and a separate reply_to; Laravel resolves config('mail.from') and per-mailable $from/$replyTo. Map them, don't merge them.$mailable->render() in Tinker, or a dev-only preview route) before you ever hit "send." Inspecting the raw HTML is ten times faster than debugging a delivery provider. Snapshot tests catch the dark-mode and Outlook regressions you can't see in a browser.How can I help you explore Laravel packages today?