netbull/foundation-emails-bundle
Netbull Foundation Emails Bundle integrates Foundation for Emails into Symfony, helping you build responsive HTML emails with Inky components and bundled assets. It streamlines template setup, styling, and email rendering for PHP projects.
netbull/foundation-emails-bundle is a Symfony bundle that brings ZURB's Foundation for Emails (the Inky component language plus CSS inlining) into Twig. You write <container><row><columns> in a template, and the bundle compiles it to Outlook-safe table HTML before you hand it to Symfony Mailer. It is not a Laravel package — there is no Blade integration or service provider — so a Laravel team is really evaluating whether the underlying renderer is worth porting.
Prerequisites: PHP 8.1+, Symfony 5.4/6.x, Twig. Flex is recommended.
composer require netbull/foundation-emails-bundle
// config/bundles.php — Flex usually adds this automatically
NetBull\FoundationEmailsBundle\NetBullFoundationEmailsBundle::class => ['all' => true],
# config/packages/netbull_foundation_emails.yaml
# Read DependencyInjection/Configuration.php for the exact keys in your version.
netbull_foundation_emails:
# stylesheet paths, inlining options, template locations, etc.
Where to look first, in this order:
DependencyInjection/Configuration.php — the actual config surface; it is small and authoritative.Resources/config/services.yaml — service IDs; note the renderer service alias you should autowire.Tests/ or README fixtures — the only reliable example of expected output.First use case: a password-reset or order-confirmation email. Put templates under templates/emails/, extend a shared layout that carries the Foundation CSS, render through the bundle renderer, then set the result as the HTML body.
{# templates/emails/order.html.twig #}
{% extends 'emails/layout.html.twig' %}
{% block body %}
<container>
<row>
<columns small="12" large="8">
<h1>Order #{{ order.number }}</h1>
</columns>
</row>
</container>
{% endblock %}
public function send(Order $order): void
{
$html = $this->foundationRenderer->render('emails/order.html.twig', ['order' => $order]);
$email = (new Email())
->to($order->customer->email)
->subject('Your order')
->html($html);
$this->mailer->send($email);
}
emails/layout.html.twig containing the doctype, <style> block, MSO conditional comments, and preheader. Every transactional email extends it. This is the single biggest consistency win.Twig\Environment::render(). Plain Twig rendering returns raw output; only the bundle path (or a decorated renderer) performs Inky conversion and CSS inlining. Bypass it and <row>/<columns> reach the inbox as unknown tags.MailerInterface keeps controllers thin and makes emails unit-testable. Inject it into listeners/handlers rather than calling Twig from controllers.<button>, <callout>, <spacer> are the elements most often inconsistent across a template set. Define them once as Twig macros and import them.foundation-emails CSS, keep media queries in a <style> block, and let the inliner handle the rest. Inline only the components you actually use — the full CSS is large.->text(...) or Symfony Mime's auto-generation). Many clients and spam filters reward it, and it is trivial to add.role="presentation" present, no remaining <columns>, expected copy present. A golden-file/snapshot test catches inliner regressions. Add a dev-only preview route that renders sample emails to the browser; use Litmus or Email on Acid for the real client matrix.foundation-emails npm) and inline with tijsverkoyen/css-to-inline-styles. More moving parts, but no Symfony dependency and no AGPL question.composer.lock, and be ready to fork. Sanity-check the "last release 2026-07-10" date against Packagist tags.config/*.php config. In Laravel you are either extracting the renderer or reimplementing it; composer require alone will not "just work".bin/console cache:clear; run cache:warmup on deploy. Stale compiled Twig templates can mask changes to Inky handling — clear the Twig cache first when debugging tag output.<columns> into <columns>, you are almost certainly rendering through the wrong path. Use |raw only for trusted content; user-supplied values stay escaped.@media and pseudo-classes. Keep responsive rules in a <style> in the head and confirm the bundle's inliner preserves them; Gmail and Outlook treat <style> differently.px, not rem. MSO conditional comments (<!--[if mso]>) can be dropped by sanitizers or misplaced by Twig blocks — keep them in the layout, not in per-email blocks.Mailer::send(). To isolate the inliner, render with inlining disabled and diff. Unconverted tags point at service wiring; missing styles point at stylesheet config paths.config/bundles.php, add it manually; otherwise services are missing and autowiring fails with a confusing "cannot autowire" error.How can I help you explore Laravel packages today?