dansmaculotte/laravel-mail-template
Store editable Laravel mail templates in the database and send them through Laravel's mail system. Define subject and Blade/HTML bodies, pass variables into templates, preview rendered output, and update email content without redeploying or hardcoding every message. Includes model and migration.
Install the package
composer require dansmaculotte/laravel-mail-template
Laravel auto-discovery should register the service provider; if you're on an older version, add it to config/app.php manually.
Publish and migrate
php artisan vendor:publish --provider="Dansmaculotte\MailTemplate\MailTemplateServiceProvider"
php artisan migrate
This typically publishes a config file (mail template settings) and a migration that creates the mail_templates table. Check database/migrations after publishing to confirm the schema — it's the source of truth for the columns you'll be populating.
First use case — send a templated email
Create a template record with a key/slug, a subject, and a Blade body containing placeholders. Then send it in code rather than authoring a Mailable:
MailTemplate::send('welcome', $user->email, [
'name' => $user->name,
'login' => route('login'),
]);
(The exact facade/helper name — MailTemplate vs. a Mailable subclass — is what you should verify first in the package's README and src/.)
Where to look first
config/mail-template.php (published) for driver, table, and default sender options.config/mail.php — if the package registers as a mail driver, this is where you switch the default mailer or define an mailtemplates mailer.src/ for the transport or Mailable class to understand how variables are injected and rendered.Database-backed templates instead of code. Treat the mail_templates table as your source of truth for copy. Product/marketing can update subject lines and body copy without a deploy; your code only passes a stable key.
Render Blade with explicit data whitelists. Pass a flat associative array of variables and document what each template expects. Keep a templates map in a service class (or config) that defines allowed variables per key, so a missing variable fails loudly rather than silently producing empty text.
Wrap in a small application-level service. Rather than calling the package directly throughout your codebase, create App\Mail\TemplatedMailer with methods like sendWelcome(User $user) and sendInvoice(Invoice $invoice). This gives you a single place to add logging, fallbacks, and data shaping — and insulates you if the package's API changes.
Queue everything. Use Mail::to(...)->queue(...) or the package's queue-aware method (verify). Since templates live in the DB, a queued job that re-reads the template will always send the latest copy — a nice operational win over compiled mailables.
Preview via a route. Add an admin-only route that renders a template with sample data (return view(...) or a preview method) so editors can validate Blade syntax and variable substitution before it goes live.
Seed templates per environment. Keep a MailTemplateSeeder with idempotent updateOrCreate calls keyed by slug. This makes templates versioned in git while still living in the DB, and lets staging/prod bootstrap consistently.
Notifications integration. For Laravel notifications, add a toMailTemplate($notifiable) method that returns the package's mailable with the right key and variables, keeping notifications consistent with direct sends.
Publish before you migrate. The migration is only available after vendor:publish. If you see "table does not exist," you haven't published.
Missing variables. Blade's $var on an undefined variable throws in strict setups and renders empty otherwise — either way the email is broken. Always pass a complete data array, and consider a str_replace-based sanity check or view()->share defaults in your mailer wrapper.
Escaping. User-supplied variables passed as {{ $name }} are HTML-escaped; {!! $body !!} is not. If templates are editor-managed, treat stored HTML as trusted, and never inject user input via {!! !!}.
Caching. If templates are read from the DB on every send, that's fine for low volume but adds a query per email. If you add caching, remember to bust the cache when an admin updates a template — a common source of "my change didn't go out" bugs.
Mail driver vs. Mailable confusion. The description calls it a "mail template driver," but many such packages ship a Mailable-style wrapper instead of a true Transport. Confirm which by reading the service provider's register()/boot(). If it's a real driver, wire it in config/mail.php:
'mailers' => [
'mailtemplates' => ['transport' => 'mail-template', ...],
],
Testing. Use Mail::fake() and assert on the mailable class or the rendered HTML (Mail::assertSent(..., fn ($m) => str_contains($m->render(), $expected))). For templates, add a test that loads every seeded template and asserts it renders without exceptions — cheap coverage that catches broken Blade early.
Queue serialization. If your mailable captures a model or a template object, ensure it's SerializesModels-safe. Passing just the template key (string) plus a scalar data array is the most robust choice.
Sender / from address. Set defaults in config/mail-template.php and override per send only when necessary; mismatched from vs. SMTP envelope is a frequent deliverability headache.
Version pinning. At 35 stars this is a small, low-maintenance package. Pin to a known-good tag in composer.json and check the package's tests (or open issues) against your Laravel version before upgrading — breaking changes are more likely to go undocumented.
How can I help you explore Laravel packages today?