spatie/laravel-mail-preview
Laravel mail transport to preview sent emails locally. Adds an in-browser overlay with a link to the last sent message, and lets you view rendered mail content in the browser. Ideal for development/testing; avoid in production.
composer require spatie/laravel-mail-preview
config/mail.php:
'mailers' => [
'smtp' => [
'transport' => 'preview',
// ... other SMTP config
],
],
app/Http/Kernel.php:
protected $middlewareGroups = [
'web' => [
// ... other middleware
\Spatie\MailPreview\Http\Middleware\AddMailPreviewOverlayToResponse::class,
],
];
routes/web.php:
Route::mailPreview(); // Default: `/spatie-mail-preview`
Send a test email via a Mailable:
Mail::to('test@example.com')->send(new TestEmail());
storage/email-previews with names like:
1712345678_test@example.com_subject.html.preview transport in config/mail.php for local testing.Mail::send()./spatie-mail-preview to browse all stored emails.SentMails facade in tests:
SentMails::assertLastContains('Expected text');
SentMails::assertSent(fn(SentMail $mail) => $mail->hasTo('test@example.com'));
'enabled' => env('APP_DEBUG') in config/mail-preview.php.storage_path to use a different directory:
'storage_path' => storage_path('custom-email-previews'),
MailStoredEvent to log or process emails:
Event::listen(MailStoredEvent::class, function ($event) {
Log::info('Email saved:', [
'html' => $event->pathToHtmlVersion,
'eml' => $event->pathToEmlVersion,
]);
});
SentMails::assertCount(1);
SentMails::last()->assertSubjectContains('Welcome');
Mail::fake() for unit tests, then use SentMails for assertions:
Mail::fake();
$this->post('/send-email');
SentMails::assertSent(fn(SentMail $mail) => $mail->bodyContains('Success'));
APP_DEBUG=false. Ensure 'enabled' => env('APP_DEBUG') in config.filename_date_format (e.g., 'Uu') to avoid collisions with identical subjects/recipients.web middleware (e.g., auth) to avoid blocking the overlay for unauthenticated users.Kernel.php.mailPreview() is added to web.php.APP_DEBUG=true in .env.storage_path in config/mail-preview.php and permissions:
mkdir -p storage/email-previews && chmod -R 775 storage/email-previews
SentMails::all() to inspect stored emails:
dd(SentMails::all()->first()->body());
php artisan vendor:publish --tag=mail-preview-views
Override resources/views/vendor/mail-preview/overlay.blade.php or preview.blade.php.MailStoredEvent to add metadata:
Event::listen(MailStoredEvent::class, function ($event) {
$event->message->getHeaders()->addTextHeader('X-Custom-ID', '123');
});
Route::get('/api/emails', function () {
return SentMails::all()->map(fn(SentMail $mail) => [
'subject' => $mail->subject(),
'url' => route('mail.preview', $mail->filename()),
]);
});
storage:link and schedule cleanup via Artisan::call('mail:cleanup'):
// In a console command
$files = Storage::files('email-previews');
foreach ($files as $file) {
if (now()->diffInSeconds($file->lastModified()) > config('mail-preview.maximum_lifetime_in_seconds')) {
Storage::delete($file);
}
}
MAIL_PREVIEW_SHOW_LINK=false in .env for faster CI runs.Mail::to()->send() directly in tests.->with() or ->render() in Mailables:
$this->markdown('emails.test')->with(['data' => $data]);
How can I help you explore Laravel packages today?