Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Foundation Emails Bundle Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

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:

  1. DependencyInjection/Configuration.php — the actual config surface; it is small and authoritative.
  2. Resources/config/services.yaml — service IDs; note the renderer service alias you should autowire.
  3. The Twig extension / token parser (if present) — determines whether Inky tags are compiled at Twig compile time or converted at render time; this affects caching and escaping.
  4. 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);
}

Implementation Patterns

  • Layout + blocks. Keep one 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.
  • Use the bundle renderer, not 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.
  • Wrap sending in a notifier service. A small class that takes the renderer plus MailerInterface keeps controllers thin and makes emails unit-testable. Inject it into listeners/handlers rather than calling Twig from controllers.
  • Macros/components for repeated pieces. <button>, <callout>, <spacer> are the elements most often inconsistent across a template set. Define them once as Twig macros and import them.
  • CSS strategy. Author against Foundation's 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.
  • Always ship a text alternative (->text(...) or Symfony Mime's auto-generation). Many clients and spam filters reward it, and it is trivial to add.
  • Testing. Render to a string and assert on the compiled output: 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.
  • Queue the send. Rendering is CPU-ish (template compile + inlining). Push it onto Symfony Messenger so it does not block the request.
  • Laravel escape hatch. If you only want the conversion logic in a Laravel app, compile Inky in a Node step (foundation-emails npm) and inline with tijsverkoyen/css-to-inline-styles. More moving parts, but no Symfony dependency and no AGPL question.

Gotchas and Tips

  • License: AGPL-3.0. This is the biggest practical issue. AGPL is strong copyleft; running it as part of a network service can trigger source-disclosure obligations. For a commercial SaaS, get legal review before adopting. For internal-only or open-source projects it is fine. A permissively licensed alternative or a small in-house inliner is often cheaper than the legal conversation.
  • Health signal: 0 stars, 0 dependents, sparse history. Treat it as a small unmaintained utility. Pin an exact version, commit composer.lock, and be ready to fork. Sanity-check the "last release 2026-07-10" date against Packagist tags.
  • Symfony-only. No Blade, no Laravel service provider, no config/*.php config. In Laravel you are either extracting the renderer or reimplementing it; composer require alone will not "just work".
  • Config caching. After changing bundle config run 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.
  • Escaping. Inky tags must be emitted as markup, not escaped. If autoescape turns <columns> into &lt;columns&gt;, you are almost certainly rendering through the wrong path. Use |raw only for trusted content; user-supplied values stay escaped.
  • Inliner vs. media queries. Most CSS inliners strip or mangle @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.
  • Outlook quirks. Use 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.
  • Debugging. Dump the rendered HTML before 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.
  • Extension points. Decorate the renderer service to post-process HTML (link tracking, UTM params, unsubscribe footer) and add a Twig extension for project-specific components. Prefer decoration over forking so upgrades stay possible.
  • Bundle registration. If Flex does not add it to config/bundles.php, add it manually; otherwise services are missing and autowiring fails with a confusing "cannot autowire" error.
  • Email size. Foundation's full CSS is heavy. Keep total HTML under ~100KB to avoid Gmail clipping, and check the "View original" size before shipping a new template.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle