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

Mjml Bundle Laravel Package

notfloran/mjml-bundle

A Symfony bundle that integrates MJML into PHP apps, compiling MJML markup into responsive HTML emails. It wraps the MJML renderer with configurable options and a simple service for generating templates, making it easy to build maintainable, mobile-friendly transactional emails in Symfony projects.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. This package is a renderer adapter around the MJML compiler — a Node.js tool that transpiles declarative MJML markup into responsive, email-client-safe HTML. v3.11.0's single change ("Adapt BinaryRenderer for MJML 5") tells you the integration seam: the package shells out to a locally installed mjml binary and parses its output. That is a Node runtime dependency bolted onto a PHP stack, and it is the central architectural decision. Two viable shapes: (a) build-time rendering — compile MJML in CI into Blade partials, zero Node in production; (b) runtime rendering — Node + mjml present in every PHP container or behind an HTTP render service. For a Laravel product with mostly static transactional templates, (a) is strictly better; runtime rendering only earns its cost when templates are user-authored or parameterized per request.

Note also that notFloran/mjml-bundle is a Symfony bundle, not a Laravel package. It is usable from Laravel only by autoloading the renderer classes directly and binding them in a service provider, bypassing the bundle's Symfony DI/config layer.

Integration feasibility. Straightforward with caveats. Composer install, Node 18/20 LTS in the image, npm i -g mjml (pinned). The renderer API surface is small — a render call taking MJML source and returning HTML. Risks are: (1) Symfony component version constraints can collide with the Symfony packages Laravel already requires; audit composer why-not before committing; (2) the composer package version and the globally installed mjml binary version are independent — nothing in the release note suggests the adapter detects the binary version, so the adaptation is likely a hard-coded assumption about MJML 5's CLI invocation/output shape; (3) MJML 5 is not broadly GA, so "adapting for MJML 5" may mean tracking a moving target.

Technical risk. Medium-high, concentrated in operations rather than code. Specific risks: silent version skew (package adapted for MJML 5 CLI, image ships MJML 4, or vice versa → wrong output or non-zero exit swallowed); no documented fallback path for MJML 4 in this release; process-spawn cost per render (typically hundreds of ms, CPU-bound); command-injection surface if template content or user-supplied parameters reach the binary without escaping via argument arrays; and mj-include/mj-raw giving template authors filesystem-read and arbitrary-HTML primitives. Add maintainer bus-factor risk — this is a small single-maintainer project, and the release is a one-line changelog with no migration notes.

Key questions.

  • Is MJML 5 GA or pre-release at the time of this release? Does v3.11.0 require MJML 5, or does it detect 4 vs 5?
  • What is the supported version matrix — package version × mjml binary version × PHP version?
  • Are there breaking changes to the BinaryRenderer public API, or is this internal only? Nothing in the changelog says.
  • Is there a non-binary renderer (e.g., HTTP API, pure-JS-in-PHP) as an escape hatch?
  • Does the package validate MJML before shelling out, and does it surface stderr/exit codes distinctly from "rendered successfully"?
  • What are the Symfony component version constraints, and do they conflict with our Laravel version?

Integration Approach

Stack fit. Laravel 10/11 on PHP 8.2+, Composer, Node 20 LTS, mjml CLI pinned via a package-lock.json baked into the image. Treat the package as a thin library, not a framework integration: composer require notFloran/mjml-bundle, then in a service provider bind an MjmlRenderer interface to the package's renderer, configured with the binary path from config/mail.php or a dedicated config file. Do not attempt to register the Symfony bundle itself.

Migration path. Default to build-time. A CI job renders resources/mjml/*.mjml into resources/views/emails/*.blade.php, committed or built into the release artifact; Mailables then reference plain Blade views. This deletes the Node dependency from production entirely and makes renders deterministic and diffable in code review. Only if a requirement appears for per-request or per-tenant template content should you stand up runtime rendering — and then behind the same MjmlRenderer interface, so the switch is a config change.

Compatibility. Pin both sides: notFloran/mjml-bundle to ^3.11 and the mjml binary to the exact major it targets. Before merging, run composer why-not symfony/* against our Laravel version. Build a golden-template corpus (10–15 representative templates covering Outlook-conditional and dark-mode cases) and diff MJML 4 output against MJML 5 output; email HTML diffs are noisy but layout regressions in Outlook are the ones that matter.

Sequencing.

  1. Spike branch: install pinned to MJML 5, render the golden corpus, capture output.
  2. Diff against current MJML 4 renders; catalogue regressions.
  3. Containerize and pin Node + mjml; add a startup healthcheck asserting the binary version matches the expected major.
  4. Wrap behind MjmlRenderer; implement both adapters (binary, HTTP service) even if only one is live.
  5. Add contract tests asserting structural invariants on rendered HTML for the highest-value emails (password reset, receipts, order confirmation).
  6. Roll out build-time rendering first; runtime rendering only behind a feature flag.

Operational Impact

Maintenance. Two dependency graphs to patch on two cadences — Composer (PHP/CVE) and npm/Node (CVE, plus MJML's own release churn). MJML 5 pre-GA status means upstream instability propagating into your build; a minor MJML bump can change rendered markup. Budget for pinning and periodic, deliberate upgrades rather than continuous tracking.

Support. The real support load is not PHP, it is email client rendering — Outlook's Word engine, Gmail's CSS stripping, dark-mode inversions. Expect template-author questions and "the email looks broken in X" tickets that are not application bugs. Mitigate with a preview route (render template → open in browser and in a Litmus/Email on Acid run) and a documented component library.

Scaling. Process-per-render is CPU-bound and does not share well. At meaningful volume, push renders onto a Laravel queue, cache rendered HTML keyed by hash(template_source + params), and prefer a shared render service over installing Node in every worker. Build-time rendering sidesteps all of this: renders happen once per deploy, scaling cost is zero.

Failure modes.

  • Binary missing or wrong major → hard failure, or worse, plausible-but-wrong HTML if exit codes aren't checked.
  • stderr captured but not surfaced → debugging blind.
  • Timeout on large templates with deep partials → queue worker hangs.
  • Invalid MJML → partial/garbled HTML emitted with success status.
  • Injection: unescaped template parameters reaching the shell; mj-include enabling arbitrary file reads; mj-raw enabling arbitrary HTML. Constrain who can author templates and validate/whitelist include paths.
  • Output drift after an unpinned MJML upgrade → emails silently degrade in Outlook only.

Ramp-up. Low for template authors — MJML is declarative and readable, a day to productivity. Moderate for engineers: Node in Docker/CI, binary version pinning, and the build-time vs runtime decision. Realistic budget: 1–2 days spike, ~1 week to productionize with pinning, golden tests, and either a CI render step or a queued render path.

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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager