jeffersongoncalves/filament-mail
Complete email management UI for Filament, built on laravel-mail. Browse and resend mail logs with HTML preview and attachments, edit multi-locale database templates with version history and live preview, send test emails, and manage delivery analytics and suppressions.
Installation
composer require jeffersongoncalves/filament-mail
Publish the package assets and config:
php artisan vendor:publish --provider="JeffersonGoncalves\FilamentMail\FilamentMailServiceProvider" --tag="filament-mail-config"
php artisan vendor:publish --provider="JeffersonGoncalves\FilamentMail\FilamentMailServiceProvider" --tag="filament-mail-assets"
Run Migrations
php artisan migrate
Register the Plugin
Add to app/Providers/Filament/AdminPanelProvider.php:
return [
// ...
'plugins' => [
\JeffersonGoncalves\FilamentMail\FilamentMailPlugin::make(),
],
];
First Use Case
Upgrading from < 1.5.0: the new Campaigns page requires
jeffersongoncalves/laravel-mail ^1.4with campaigns enabled. Bump the dependency and confirm the feature flag before pulling this release:composer require jeffersongoncalves/laravel-mail:^1.4
Template Management
en, pt).
// Define a template in a migration or seeder
\JeffersonGoncalves\LaravelMail\Models\Template::create([
'name' => 'Welcome Email',
'subject' => 'Welcome to Our Platform',
'html' => '<h1>Hello, {name}!</h1>',
'locales' => json_encode(['en' => ['subject' => 'Welcome...'], 'pt' => ['subject' => 'Bem-vindo...']]),
]);
{{ template('footer') }}.Email Sending
use JeffersonGoncalves\LaravelMail\Facades\LaravelMail;
LaravelMail::send([
'template' => 'welcome_email',
'to' => '[email protected]',
'data' => ['name' => 'John Doe'],
]);
Analytics & Tracking
mail.sent or mail.opened events:
event(new \JeffersonGoncalves\LaravelMail\Events\MailSent($mail));
Campaigns & Per-Campaign Reporting (new in 1.5.0)
LaravelMail::send([
'template' => 'welcome_email',
'to' => '[email protected]',
'data' => ['name' => 'John Doe'],
'tags' => ['spring_promo_2025'], // becomes the campaign grouping key
]);
{product}_{goal}_{yyyy_mm}) — otherwise spring-promo and Spring_Promo show up as two separate campaigns.newsletter_control) permanently in the list as a baseline.Suppression Lists
$suppression = \JeffersonGoncalves\LaravelMail\Models\Suppression::create([
'email' => '[email protected]',
]);
Mail::to($user)->queue(
(new CampaignMailable($user))->tag('onboarding_step_2')
);
config/filament-mail.php to queue emails by default:
'queue' => true,
Mailable class to integrate with existing Laravel mailables:
use JeffersonGoncalves\LaravelMail\Mailables\Mailable;
class CustomMailable extends Mailable
{
public function build()
{
return $this->subject('Custom Subject')->view('emails.custom');
}
}
resources/lang/{locale}/filament-mail.php):
return [
'templates' => [
'welcome_email' => [
'subject' => 'Personalizado para {locale}',
],
],
];
Campaigns Page Is Empty After Upgrading to 1.5.0
jeffersongoncalves/laravel-mail is still < 1.4 — the page renders but there is no campaign data to read.laravel-mail's config. The release notes are explicit: the feature "requires jeffersongoncalves/laravel-mail ^1.4 with campaigns enabled". Enable it there and clear config cache:
php artisan config:clear
filament-mail flag — don't go hunting in config/filament-mail.php for it.Historical Sends Don't Appear as Campaigns
Migration Conflicts
laravel-mail, ensure the templates and mailables tables are not duplicated. Run:
php artisan vendor:publish --tag="laravel-mail-migrations" --force
before migrating.Template Caching
php artisan view:clear
Queue Stuck Emails
php artisan queue:restart
Multi-Locale Sync
config/app.php under locales. Missing locales may break template rendering.storage/logs/laravel-mail.log for rendering errors.config/filament-mail.php:
'tracking' => [
'events' => false,
],
Remember that disabling tracking events also blanks out campaign rates — the Campaigns page reads the same events, so don't debug "zero opens" with tracking switched off.<a href> in the rendered HTML, not injected by JS after load.en and pt, split them into separate tags if you want to compare them.Custom Fields
// app/Providers/FilamentMailServiceProvider.php
FilamentMail::registerTemplateField(
'dynamic_content',
\JeffersonGoncalves\FilamentMail\Fields\DynamicContentField::class
);
API Access
// routes/api.php
Route::middleware('auth:sanctum')->post('/send-mail', [\App\Http\Controllers\MailController::class, 'send']);
campaign/tag field in the request payload — otherwise API-triggered sends silently fall outside the Campaigns page.Webhook Integration
// app/Providers/EventServiceProvider.php
protected $listen = [
\JeffersonGoncalves\LaravelMail\Events\MailOpened::class => [
\App\Listeners\UpdateTracking::class,
],
];
Bulk Actions
// app/Providers/FilamentMailServiceProvider.php
FilamentMail::tableActions([
Tables\Actions\BulkAction::make('suppress')
->action(function (Collection $records) {
$records->each->suppress();
}),
]);
config/filament-mail.php:
'locales' => [
'fallback' => 'en',
],
'assets' => [
'path' => 'cdn.example.com/storage/filament-mail',
],
config/filament-mail.php controls the UI; campaign capture itself is configured in laravel-mail. When the Campaigns page misbehaves, check laravel-mail first — and remember php artisan config:clear after toggling either.How can I help you explore Laravel packages today?