andreaselia/filament-laravel-analytics
Filament plugin that brings Laravel Analytics into your admin panel, letting you view Google Analytics data without leaving Filament. Adds analytics widgets and pages so you can display traffic, visitors, and other key metrics directly in your dashboard.
This package renders Google Analytics (GA4) traffic widgets inside a Filament panel. It is a thin display layer over spatie/laravel-analytics, so the real setup work is on the Google side, not the Laravel side.
Install it and check the transitive dependency.
composer require andreaselia/filament-laravel-analytics
Confirm spatie/laravel-analytics landed in composer.json. If the plugin pulled an old or oddly pinned version, require it explicitly so you control the upgrade path.
Provision credentials before writing any panel code. This is where most of the time goes:
UA- tracking ID will not work.Publish and configure.
php artisan vendor:publish --tag="filament-laravel-analytics-config"
// config/analytics.php
'property_id' => env('ANALYTICS_PROPERTY_ID'),
'service_account_credentials_json' => storage_path('app/analytics/service-account-credentials.json'),
'cache_lifetime_in_minutes' => 60 * 24,
Register the plugin in the panel provider that should see analytics (check the README for the exact plugin class/namespace for your version — it changed across early releases):
public function panel(Panel $panel): Panel
{
return $panel->plugin(FilamentLaravelAnalyticsPlugin::make());
}
Prove the Google pipe works before debugging Filament. In tinker or a test, call the underlying package directly:
use Spatie\Analytics\Facades\Analytics;
use Spatie\Analytics\Period;
Analytics::fetchTotalVisitorsAndPageViews(Period::days(7));
If that throws, the problem is credentials/permissions and no amount of widget debugging will help.
First use case: a stakeholder-facing "Traffic" page on the dashboard — visitors and page views for the last 30 days plus a top-pages list. That single page is most of the day-to-day value; build it first and treat everything else as additive.
Panel-scoped, one property per panel. Register the plugin only on the panel that should show analytics. In multi-panel or multi-tenant apps, don't make the property ID dynamic on the fly — register the plugin conditionally per panel and keep the property in config/env. Per-tenant properties get painful fast because the service account must be a Viewer on every property.
Use the bundled filters before writing code. v1.1.0 added filtering (e.g. date-range selection) and merged the views into a single Blade file. For a standard "traffic overview," the built-in filters are usually enough. Practical consequences:
cache_lifetime_in_minutes meaningful (a day or so) and use a shared cache store if you run more than one web node.Split the "canned page" from custom charts. Use the plugin for the standard page; for anything bespoke (funnels, conversion widgets, per-post page views) write your own Filament widget that calls spatie/laravel-analytics directly. Mixing both is the pragmatic choice: you get the free page and full control where it matters, without forking vendor code.
Cache deliberately — quota is the real constraint.
// config/analytics.php
'cache' => [
'store' => env('ANALYTICS_CACHE_STORE', 'redis'),
],
GA4 quota errors are the most common production incident with this stack. A shared, persistent store is the cheapest insurance: array in tests, redis/database in production, never file on a multi-node deploy.
Keep analytics out of local and non-production environments. Analytics calls in local either fail (no credentials) or burn quota. Gate registration:
->plugin(FilamentLaravelAnalyticsPlugin::make())
// ...
->when(fn () => app()->isProduction(), ...)
and hide the page from non-admins via a policy, gate, or canAccessPanel.
Authorization is on you. The plugin exposes navigation/page config, but access control is not implicit. Define a gate (e.g. Gate::define('viewAnalytics', ...)) and guard the page/widget so analytics isn't visible to every panel user by default.
Treat it as a read-only convenience layer. For anything requiring first-party event data — funnels, cohorts, retention — this package is the wrong tool; a dedicated product-analytics platform belongs there. Keep the boundary explicit so nobody tries to force it into a reporting system of record.
User does not have sufficient permissions for this property almost always means the service-account email wasn't added as a Viewer on the GA4 property, or you're using a UA- property ID instead of the numeric GA4 ID.SERVICE_DISABLED means the API isn't enabled. A 403 PERMISSION_DENIED containing that string means the Analytics Data API wasn't turned on in the Cloud project. Easy to miss because the credentials JSON is otherwise valid.storage_path(...) is safest. Relative paths resolve from the working directory and break under queue workers, Octane, or a different deploy root.--force, diff, and re-apply customizations, or your overrides will silently stop being used.ANALYTICS_CACHE_STORE=array or clear the analytics cache while iterating, otherwise you'll chase phantom "my change didn't apply" bugs caused by a 24-hour cached response.Analytics::fetchTopPages(...) in tinker or a test and log the exception. Filament's error surface hides the underlying Google client message, which is where the actionable detail lives.composer.json, treat view overrides as something to re-check on every bump, and budget for vendoring or forking if it becomes load-bearing.How can I help you explore Laravel packages today?