nette/mail
Nette Mail is a simple, flexible PHP library for sending emails. It supports SMTP, sendmail, and PHP mail, with a clean API for building messages, attachments, HTML bodies, and multiple recipients.
Architecture fit. nette/mail is a message-composition and delivery library, not a mail framework. In a Laravel codebase this creates an immediate overlap: Illuminate\Mail + Symfony Mailer already own transport abstraction, Blade/Markdown rendering, queued Mailables, events, Mail::fake(), alwaysTo, and log/array drivers. The defensible fit is therefore component-level, not framework-level. The genuinely additive pieces are narrow: CssInliner (v4.1.0, with Outlook-compatible bgcolor/width/align attributes and @media preservation), HtmlComposer (v4.1.2: standalone inline-CSS + image-embed + <title>→subject + plain-text alternative pipeline), and the MIME/encoding correctness backlog evidenced by the v4.1.1 fixes. The Message/SmtpMailer/DKIM layer is only interesting if we deliberately want to own message construction.
Integration feasibility. Two viable paths.
(a) Surgical: call CssInliner/HtmlComposer inside a Mailable's build() or a render-stage decorator. No transport change, no queue change, no loss of Laravel events. Roughly a 1–2 day spike plus test coverage.
(b) Transport swap: register a custom Laravel mail driver wrapping Nette\Mail\SmtpMailer. Technically possible, but we forfeit queued Mailable retries, Mail events, Mail::fake(), and per-message logging unless we re-implement them. High cost, low incremental value over Symfony's SMTP transport.
Technical risk.
NOASSERTION means GitHub could not classify it. Nette packages are conventionally BSD-3-Clause (with a GPL dual option), but "conventionally" is not legal clearance. This must be resolved before any code lands — it is a binary blocker, not a risk to manage.MimePart is a real long-term tax.250-AUTH-over-TLS fix (#67/#71) — that negotiation gap appears unresolved.nette/utils ^4.1, ext-iconv) constrains us if any service is still on 8.0/8.1.Key questions.
CssInliner demonstrably beat the existing tijsverkoyen/css-to-inline-styles path on that corpus?Stack fit. Laravel 11+/PHP 8.2+ is the target. nette/mail slots in as a render helper, not a runtime dependency of the mail pipeline. ext-dom (for CssInliner selector matching on PHP 8.4+) and ext-intl (IDN addresses) should be declared explicitly in the container image.
Migration path. Do not migrate. Add. Keep Illuminate\Mail as the delivery and queueing backbone; insert Nette at the HTML-preparation boundary. This is an additive dependency behind an interface — e.g. HtmlPreparer with a Nette-backed implementation and the current implementation retained — so rollback is a config flag, not a revert.
Compatibility. Verify against the pinned Laravel and Symfony Mailer versions in composer.lock; watch nette/utils ^4.1 for transitive conflict with our existing Nette-family dependencies (Tracy, if any, is the likely collision). Confirm ext-iconv is present in all runtime images.
Sequencing.
CssInliner as the default for new templates only; keep legacy templates on the existing path.HtmlComposer for plain-text alternatives and <title>→subject; adopt only if it removes hand-maintained .txt templates.SmtpMailer/DKIM only if we decide to own the SMTP edge. Default answer is no; route through an ESP.Maintenance. Nette tracks PHP releases aggressively (8.5 in v4.0.4, 8.6 in the stated requirements range) — good hygiene, low abandonment risk. But releases are frequent and behavior-changing minor versions exist (v4.2.0 → v4.2.1 quoting regression), so pin exact versions, treat patch upgrades as change-managed, and maintain a golden-template snapshot test that diffs generated MIME output.
Support. No commercial support tier, no Laravel-oriented documentation. The Nette manual is terse and assumes Nette ecosystem familiarity. Expect our team to be effectively self-supporting; budget for one engineer to become the internal owner.
Scaling. SmtpMailer is a synchronous socket client; persistent connection support exists but there is no pooling, batching, or backpressure. Behind our queue this is fine. Sending inline from a web request is not acceptable — same rule as today. FallbackMailer provides failover between mailers, not retry-with-backoff; do not mistake it for resilience.
Failure modes to instrument. (1) SMTP slow-server empty responses — recurring enough to have been patched twice (v3.1.11, v4.0.2); enforce explicit timeouts. (2) 250-AUTH advertised only post-TLS — fix reverted in v3.1.4; test against our relay. (3) HTML regex edge cases — large data: URIs, filenames with spaces/parentheses, and address phrases with ,/;/@ have all broken recently; fuzz the template corpus. (4) Sendmail -f / mail() availability assumptions — irrelevant if we stay on Symfony transport, which is another argument for staying. (5) Custom-path sends will not emit Laravel MessageSending/MessageSent events unless wired manually — observability gap that must be closed in the same PR.
Ramp-up. The Message API is small and fluent; a mid-level engineer is productive in under a day. The real ramp-up cost is organizational: the team's muscle memory is Mailable/Blade, and a second rendering path is a permanent source of "which one does this template use?" confusion. Mitigate with a hard rule — one canonical path per template, enforced in code review.
Recommendation. Proceed with a gated, two-week evaluation of CssInliner only, contingent on written license clearance and a demonstrated rendering win over the incumbent inliner. Do not adopt the transport, DKIM, or Message layer in Laravel; the overlap with Illuminate\Mail costs more than it returns. Interceptor/MailTracy are genuinely nice but duplicate Laravel's alwaysTo and Mail::fake(), so they are not a reason to adopt.
How can I help you explore Laravel packages today?