jeffersongoncalves/filament-newsletter
Filament Newsletter adds newsletter management to your Filament admin panel, letting you handle subscribers, mailing lists, and campaigns from a clean, native interface. Manage opt-ins and send updates without leaving your dashboard.
Install and register (the 15-minute path):
composer require jeffersongoncalves/filament-newsletter
php artisan vendor:publish --tag="filament-newsletter-migrations"
php artisan vendor:publish --tag="filament-newsletter-config"
php artisan migrate
Then register the plugin on the panel you want it on:
// app/Providers/Filament/AdminPanelProvider.php
public function panel(Panel $panel): Panel
{
return $panel
// ...
->plugin(FilamentNewsletterPlugin::make());
}
Where to look first, in order:
config/filament-newsletter.php — this is the real API surface for day-to-day use (model overrides, navigation, feature toggles). Read it before reading any class.FilamentNewsletterPlugin) — the fluent ->navigationGroup(), ->navigationLabel() style methods tell you what is configurable without forking.src/Filament/Resources — the Subscribers / Lists / Newsletters (campaign) resources. Almost every customisation request ("add a column", "hide the delete action", "change the form") is answered by extending these rather than editing vendor code.First use case to prove it works: create a subscriber list, add a subscriber through the panel UI, then import/seed a handful of records and confirm the table filters and search behave. That single loop exercises the migrations, the config, the resources and the underlying jeffersongoncalves/laravel-newsletter integration — if that works, the rest is configuration.
Version note for 1.1.0: the plugin now requires jeffersongoncalves/laravel-newsletter ^2.0 (up from ^1.0). Confirm your composer.lock resolves cleanly on a fresh install before you plan the upgrade, especially if you have any code touching the underlying newsletter package directly.
Two-panel split (the most common real-world layout). Marketing users live in a different panel from ops/admin. Register the plugin only where it belongs and use the plugin's navigation methods to keep it out of the way in the other panel — do not rely on conditional ->plugin() calls inside a single provider, since panel config is cached.
Extend resources, don't fork. Subclass the vendor resource and re-register it:
class SubscribersResource extends \Jeffersongoncalves\FilamentNewsletter\...\SubscriberResource
{
public static function getNavigationGroup(): ?string { return 'Marketing'; }
public static function table(Table $table): Table
{
return parent::table($table)
->columns([...parent-style columns, Tables\Columns\TextColumn::make('source')]);
}
}
Then point the plugin/config at your subclass. Guard against upgrades by keeping the overrides to additions and ->modify()-style tweaks rather than full replacements of form()/table().
Front-end capture feeding the panel. Keep public opt-in forms on a plain Laravel controller or Livewire component writing to the underlying newsletter model, not through Filament. The panel is an admin surface; routing public traffic through Filament resources is a common early mistake and makes rate limiting, honeypots and validation awkward.
Sending through the queue. Anything that dispatches mail should go through a queued job with an explicit queue name so campaign sends don't starve transactional mail:
// config/queue.php → 'connections' → separate 'newsletters' queue
SendNewsletterCampaign::dispatch($campaign)->onQueue('newsletters');
Run a dedicated worker (php artisan queue:work --queue=newsletters) and set retry_after above your longest campaign send.
Locale-aware admin. With 1.1.0 shipping 17 new locales (ar, az, de, es, fa, fr, hi, it, ja, nl, pl, pt, ru, tr, uk, uz, zh_CN), a multilingual team gets translated resource labels for free. Set app.locale per user, or use Filament's per-user locale if your panel exposes it. Verify RTL locales (ar, fa) render correctly before shipping — Filament handles RTL layouts, but any custom Blade partial you added to the resources will not flip automatically.
Override copy without editing vendor files. Publish the package translations and edit only the keys you want to change. This is the supported extension point; a translated label is not a reason to fork a resource.
Testing. Use RefreshDatabase, actingAs() a panel user, and Livewire test helpers against the resource pages. Assert on the underlying newsletter models rather than the rendered HTML — the HTML is the least stable part of a Filament plugin across minor releases.
laravel-newsletter ^1.0 → ^2.0 bump is the one thing to actually check in 1.1.0. The new translations are additive and safe, but this dependency constraint is not. If your project pins jeffersongoncalves/laravel-newsletter at ^1.0, Composer will refuse the upgrade, and any custom provider/driver code written against v1 APIs needs a read of its changelog first. Resolve this before touching anything else.zh_CN (underscore). If your app sets zh-CN (hyphen) the translation will silently fall back to English. Same class of problem with pt here versus pt_BR in many apps — check what key is actually on disk in the package's resources/lang directory and match your app.locale to it exactly.vendor:publish on 1.0.0, the 17 new locales and any corrected strings in 1.1.0 will not appear — the files in lang/vendor/... win over the package's. Republish with --force when you upgrade, and diff first if you have hand-edited strings.php artisan config:cache after editing the config file, and remember that any env() call inside the config resolves to null in a cached config on some deploy setups — put the value in the config file, not in the plugin registration.->authorize() before this reaches production.vendor/ directory is what turns the next patch release into an archaeology project. Use resource subclasses, config, and published translations only.composer update jeffersongoncalves/filament-newsletter, run php artisan migrate (usually a no-op for a translations release), republish translations with --force if you had published them, clear config:cache and view:cache, then smoke-test one list and one subscriber edit in a non-English locale.How can I help you explore Laravel packages today?