c6digital/filament-plausible-page
A Filament plugin that adds a Plausible Analytics page to your admin panel, giving you quick access to your site's traffic and stats without leaving Filament. Simple to install and configure for Laravel apps.
Install and register
composer require c6digital/filament-plausible-page
v2.3.0 is the first release with Filament v4 and v5 support, so confirm your panel's Filament constraint before installing — if you are still on Filament v3, stay on ^2.2 until you have verified v3 is still allowed by the package's composer.json (see Gotchas).
Register the plugin on the panel that should show analytics:
use C6Digital\FilamentPlausiblePage\FilamentPlausiblePagePlugin;
public function panel(Panel $panel): Panel
{
return $panel
->plugins([
FilamentPlausiblePagePlugin::make(),
]);
}
Where to look first
config/filament-plausible-page.php — publish it (php artisan vendor:publish --tag="filament-plausible-page-config") and read it before writing any code. The keys tell you whether the package is rendering a Plausible shared dashboard (embed/iframe, no API key) or querying the Stats API (API key required), plus the domain/self-hosted URL settings.src/ — this is the extension point if you need to customise what is rendered.First use case
Give non-technical stakeholders (marketing, founders, account managers) a "Traffic" or "Analytics" item in the admin sidebar so they can check Plausible without a second login, a second bookmark, or asking you for numbers in Slack. Wire it to a project/site domain via env, register only on your admin panel, and gate it to roles that should see traffic data.
Config-driven, env-per-environment
Keep the domain and credentials in .env so staging points at staging (or the plugin is disabled entirely) and production points at the live site:
// config/filament-plausible-page.php — shape, verify exact keys in the published file
return [
'domain' => env('PLAUSIBLE_DOMAIN'),
'url' => env('PLAUSIBLE_URL', 'https://plausible.io'), // self-hosted override
'shared_link'=> env('PLAUSIBLE_SHARED_LINK'),
'api_key' => env('PLAUSIBLE_API_KEY'),
];
Register conditionally per panel and environment
The plugin is presentation-only, so there is no reason for it to exist in local dev or on a customer-facing panel:
->plugins(array_filter([
app()->isProduction() ? FilamentPlausiblePagePlugin::make() : null,
]))
Or guard inside the page's canAccess()/canViewAny() against a role or permission rather than hiding the plugin, so you keep the route but control access properly (returning false from canAccess() yields a 403, not a 404).
Multi-tenant / multi-site setups
If each tenant has its own Plausible site, derive the domain from the current tenant inside the page rather than from static config:
protected function getDomain(): string
{
return Filament::getTenant()?->plausible_domain
?? config('filament-plausible-page.domain');
}
Check that the page is resolved per-request (it is a Livewire component, so this runs on each mount) and that tenant scoping is applied to the route via ->tenant() on the panel.
Compose with your own widgets
The most common real-world pattern is not "replace the dashboard" but "one analytics page next to the operational widgets". Register the plugin page alongside custom Filament widgets on a dedicated page, and let it own the traffic narrative while your widgets handle orders/signups. Keep the plugin page separate from your main Dashboard so you can apply different canAccess() rules.
Cache anything that hits the Stats API
Plausible's Stats API is rate-limited (roughly 600 requests/hour per API key). Admin users refreshing a dashboard will burn that faster than you expect. If you build on top of the package's data, wrap the call:
$stats = Cache::remember("plausible.{$domain}.30d", now()->addMinutes(10), fn () => ...);
Testing
For a smoke test in a feature test, acting as an authorised user and hitting the page is usually enough:
$this->actingAs($admin)
->get('/admin/plausible')
->assertSuccessful();
If the page renders an external embed, assert on the component/navigation rather than the iframe contents.
Verify the Filament version constraint after upgrading. "Add Filament v4 and v5 support" is a minor version bump, but it may have been implemented by widening the constraint (^3.0|^4.0|^5.0) or by replacing it. Before running composer update, check the package's composer.json (or the diff of PR #12) to confirm v3 is still supported. If it was dropped, this is effectively a breaking change for anyone on Filament v3 despite the semver number — pin ^2.2 until your panel upgrade is done.
Filament v4 moved things around. If you subclassed the page or copied it in v2.2, expect namespace churn in v4/v5 (Filament\Forms → Filament\Schemas for layout components, action/table namespace changes, ->form() → ->schema() in several places). Re-check any overridden methods against the v2.3.0 source rather than assuming your v2.2 subclass still compiles.
Rebuild assets and clear caches after upgrading. Run php artisan filament:assets, php artisan view:clear, and php artisan config:cache — a stale config cache is the #1 reason a changed domain or shared link appears to be ignored in production.
Iframes need CSP cooperation. If the package embeds a Plausible dashboard, Content-Security-Policy headers (often added by a security middleware or your web server) will silently blank it. Add your Plausible origin to frame-src (and connect-src for the API domain). For self-hosted Plausible, this includes your own analytics hostname.
Ad blockers and privacy extensions. Admin users running uBlock/Brave/NextDNS may see an empty page because requests to plausible.io get blocked client-side. Have a fallback message or a note in the page description — otherwise you will get bug reports that are not bugs.
API key scope and rate limits. A Plausible Stats API key is per-account and shared across sites; if you also use it for scheduled reporting jobs, the admin dashboard competes for the same quota. Watch for 429s in laravel.log, and consider a separate key for the panel.
Navigation duplication. Registering the plugin on multiple panels (e.g. admin and app) creates the page on both. If a panel already has a page with the same slug, you will get a route collision — set an explicit ->slug() or only register on the panel that needs it.
Extension points to reach for, in order of preference
canAccess() / getNavigationBadge() on a subclassed page.Dependabot noise. The other two entries in this release (Pint action, fetch-metadata) are CI-only and have no runtime impact — do not let them distract from the Filament support change, which is the only item that affects your application.
How can I help you explore Laravel packages today?