xammie/mailbook
Mailbook is a Laravel package for inspecting your mails without triggering them in your app. Register mailables and notifications in routes/mailbook.php, then preview them at /mailbook.
Architecture fit. Mailbook is a dev-only Laravel package (composer require --dev) that registers a service provider, an isolated route file (routes/mailbook.php), a Blade/Tailwind UI, and a config file. It requires zero changes to application mail classes, no migrations, no queue dependency, and no production footprint when correctly gated. That is a clean architectural seam: it consumes the existing Mailable/Notification contracts and the container's DI, so it degrades gracefully if removed. For a Laravel shop, fit is effectively native.
Integration feasibility. Low effort, high reversibility:
composer require --dev, php artisan mailbook:install) and one hand-authored route file.Mailbook::add() accepts a class name (DI auto-resolves constructor params) or a closure returning a configured instance, so realistic fixtures are expressible with existing factories.App\Mails mailables and App\Notifications notifications, including notifiables via ::to(), grouping/categories, and per-mail variant() scenarios.locales) and a send-to-address mode cover design review and QA sign-off without engineering intervention.Technical risk.
enabled config, auth middleware), a staging or production deploy can expose internal email templates and, worse, execute fixture closures that write to real databases.factory()->create() write to whatever DB the app points at. database_rollback (off by default) mitigates this but is a transaction rollback, not a sandbox — queued jobs, outbound HTTP calls, and external service mutations are unaffected.send mode dispatches via the default mail driver and displays a send button. If config leaks into a shared environment with production SMTP credentials, test mail goes out from a production domain. This should be treated as a hard-gated, local-only feature.ShouldQueue mailables (fixed 1.5.3) and embedded/inline attachments (fixed 1.11.1) were genuine rendering defects. Pin to a recent 1.11+ release rather than "latest minor."Key questions.
/mailbook route gated by environment and middleware by default, or must we wrap it ourselves?routes/mailbook.php and does it survive constructor signature changes to mailables?Stack fit. Native Laravel. Works alongside the existing asset pipeline (Tailwind UI is prebuilt and publishable), needs no JS toolchain, no database schema, and no queue worker. It is compatible with whatever mail transport developers already use — Mailpit/Mailhog/Log driver locally, and the send feature only if explicitly enabled. Fits existing PHPUnit/Pest CI as a smoke check (boot the app, hit the mailbook route in testing, assert 200).
Migration path. There is nothing to migrate. This is an additive, greenfield dev-tooling install with a one-line removal path (composer remove xammie/mailbook, delete routes/mailbook.php and the published config). That reversibility is the strongest argument for a low-ceremony pilot: we can trial it on one team for one sprint at near-zero switching cost.
Compatibility.
^1.11) rather than floating; the 1.10.x → 1.11.x line contains attachment-rendering fixes we want.php artisan route:cache and in CI containers, where the dev package may or may not be installed (it will be absent in --no-dev production installs, which is the desired state and incidentally our main safety mechanism).Sequencing (recommended 4-week pilot).
enabled restricted to local (optionally a dedicated staging flag), route middleware set to web + an auth gate, send disabled, database_rollback enabled. Measure install-to-first-preview time as the baseline metric.locales if we ship multi-language mail. Publish config to source control so gating is reviewable. Run one design review against Mailbook instead of screenshots and record whether it reduced back-and-forth.Deferred decision. A shared, authenticated staging instance for designers/PMs is where the value compounds but where the risk concentrates. Treat it as a separate, later decision requiring SSO, network restriction, send hard-off, and a read-only or disposable database.
Maintenance. Low but non-zero, in two places:
routes/mailbook.php is application code. Every mailable constructor change, renamed factory state, or new required dependency breaks a closure. Without an owner this file rots within two quarters and the tool is abandoned by the team before it is ever evaluated on merit. Assign ownership to the same group that owns the mail templates.Support. No vendor, no SLA, MIT, community issues only. Acceptable because the tool is developer-facing and non-production; a broken Mailbook blocks nobody's deploy. Internal support is a runbook entry plus a named owner. Do not place it on any critical path (e.g., do not make it the sole gate for release approval).
Scaling. Per-developer local usage scales linearly and harmlessly — each engineer renders their own previews. Two scaling pressures appear at team size:
category()/group() and by splitting registrations into per-domain files required from the main route file. Rendering is on-demand, so runtime cost is not the constraint; navigability is.Failure modes (ranked by severity, not likelihood).
| Mode | Impact | Mitigation |
|---|---|---|
| Route reachable in production/staging without auth | Template and internal-logic disclosure; fixture closures writing to live DB | enabled = local only; middleware + auth; assert --no-dev in prod build; add a CI test asserting 404 in production env |
send enabled against production SMTP |
Real email sent from production domain to a test address | Hard-off in config; treat enabling as a reviewed change; never on shared infra |
| Factory fixtures against shared DB | Data pollution, phantom users/orders, test-data cleanup debt | database_rollback on; point at local DB; prefer make() over create() where the mailable allows |
| Rendering side effects (jobs, external APIs, uploads) | Queued work executed, third-party calls billed or stateful | Audit registered mailables for side effects before adding; prefer pure-render mailables |
| Stale/absent fixtures after refactor | Tool silently shows fewer or broken mails; team stops trusting it | Owner + definition-of-done; a smoke test that the route returns 200 with N registered mails |
| Inline/embedded attachment rendering bug | Previews misrepresent the shipped email | Pin ^1.11 (fix landed in 1.11.1); add a mailable with an embedded image to the fixture set as a canary |
| Route caching / env inconsistency | Works locally, throws in CI or vice versa | Test the route in CI; document --no-dev behavior |
Ramp-up. Individual: under an hour to a useful first preview, half a day to be productive with variants and groupings. Team: the technical ramp is trivial; the behavioral ramp is the actual constraint — engineers must adopt "register it in Mailbook" as part of shipping a mailable, and designers must check Mailbook before requesting screenshots. Expect one sprint of deliberate reinforcement. Success metrics worth tracking during the pilot: design-QA round trips per mail change, time from mailable change to stakeholder sign-off, and percentage of mailables with at least one registered variant. If those don't move, the tool is being installed rather than leveraged — and the correct TPM action is to remove it rather than carry maintenance cost for a preview page nobody opens.
Overall TPM position. Recommend a timeboxed pilot on a single team with an explicit gating checklist and a named fixture owner. The package is small, cheap, reversible, and solves a real cross-functional friction point (engineers as screenshot middlemen for design review). The two things that would change the recommendation: a requirement to expose it to non-engineers in a shared environment (raises security scope substantially), or inability to assign ownership of the fixture file (guarantees abandonment). Do not adopt it as a testing strategy — it previews, it does not assert; keep mail rendering correctness in the test suite.
How can I help you explore Laravel packages today?