Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Plausible Widget Laravel Package

danilopolani/filament-plausible-widget

A Filament widget that brings Plausible Analytics stats into your admin dashboard, so you can view site traffic and key metrics without leaving the panel. Simple to add to any Filament dashboard.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and publish

composer require danilopolani/filament-plausible-widget
php artisan vendor:publish --tag="filament-plausible-widget-config"

As of v3.2.0 the package officially supports Laravel 12. If you're on Laravel 10/11 you're fine too — the 3.x line targets Filament 3 and the modern Laravel range. Pin the version you install (see Gotchas) so a future minor bump can't surprise you on a Friday deploy.

2. Get the two credentials you actually need

  • Site ID — the domain exactly as it appears in Plausible (example.com, not https://www.example.com). Subdomains matter.
  • Stats API key — from Plausible → Settings → API Keys. This is not your account password and not the site-specific snippet key. Confirm your Plausible plan includes Stats API access before you spend time debugging a blank widget.

Configure via .env, not by editing the published config directly:

PLAUSIBLE_API_KEY=xxxxxxxxxxxxxxxxxxxx
PLAUSIBLE_SITE_ID=example.com
# only for self-hosted:
# PLAUSIBLE_BASE_URL=https://analytics.example.com

3. Where to look first

Read the package in this order — it's small enough to skim in ten minutes:

  1. config/filament-plausible-widget.php — the only real API surface. Every knob lives here.
  2. src/Widgets/ — the shipped widget class. Copy its FQCN; that's what you register.
  3. src/ service provider — tells you what's bindable and whether the HTTP call is isolated (it should be, and that determines how easy it is to test).

4. First use case: register it on the dashboard

In app/Providers/Filament/AdminPanelProvider.php:

use Danilopolani\FilamentPlausibleWidget\Widgets\PlausibleWidget; // confirm FQCN in README

->widgets([
    AccountWidget::class,
    PlausibleWidget::class,
])

That single card — visitors / pageviews / bounce rate for the last 30 days — is the whole value proposition: your content or marketing team stops tabbing over to plausible.io. Ship that, then iterate.

Implementation Patterns

Scope the widget to the dashboard page, not the whole panel. Filament renders ->widgets() on every page that uses the dashboard layout. If you register it globally it will follow users into resource edit screens and burn API calls for no reason. Prefer registering it only where it belongs:

->widgets([
    AccountWidget::class,
    PlausibleWidget::class,
])
// and gate it:
->widgets([]) // on secondary panels

Cache the response — this is the single most important pattern. The Stats API is rate-limited and every dashboard refresh hits it. Wrap the stats call in your own cache layer if the package doesn't already, or override the data method in a subclass:

protected function getStatsData(): array
{
    return Cache::remember(
        'plausible:stats:' . config('filament-plausible-widget.site_id'),
        now()->addMinutes(15),
        fn () => parent::getStatsData(),
    );
}

15–60 minutes is the right range. Analytics data isn't real-time-critical for an admin panel.

Set a polling interval deliberately, or not at all.

protected static ?string $pollingInterval = null; // default: no polling, user refreshes

Only enable polling ('60s') if someone explicitly asked for a live view, and only with the cache above in place.

Subclass for every variation. The package almost certainly exposes the period/metrics as overridable properties or methods. Rather than forking, extend and override:

class WeeklyTrafficWidget extends PlausibleWidget
{
    protected function getPeriod(): string
    {
        return '7d'; // Plausible accepts 7d, 30d, 6mo, 12mo, custom
    }

    protected function getHeading(): string
    {
        return 'Traffic — last 7 days';
    }
}

Multiple sites = multiple subclasses. If you run several properties, create one thin subclass per site and set the site ID per class. Don't try to make one widget "smart" about which site to show — the admin dashboard isn't the place for state.

Gate by permission. Traffic data is commercially sensitive; not every admin should see it:

public static function canView(): bool
{
    return auth()->user()?->can('view analytics') ?? false;
}

Combine with internal metrics. The strongest use of this widget is as context next to your own numbers — pair the Plausible visitors card with a StatsOverviewWidget showing signups or orders from your database. Traffic without conversion is trivia.

Testing. Because the data comes over HTTP, Http::fake() should cover it. If the package calls Http directly (check src/), a feature test on the dashboard page plus a faked stats response is enough:

Http::fake(['plausible.io/*' => Http::response(['results' => [...]])]);
$this->actingAs($admin)->get('/admin')->assertOk();

Gotchas and Tips

A blank card is almost always auth, not a bug. The Stats API returns 401 for a bad/missing key and 400 for a malformed site ID, and Filament widgets swallow exceptions into an empty state. When nothing renders, Http::fake() isn't your problem — log the raw response once:

Http::withToken(config('filament-plausible-widget.api_key'))
    ->get('https://plausible.io/api/v1/stats/aggregate', [
        'site_id' => config('filament-plausible-widget.site_id'),
        'period'  => '30d',
        'metrics' => 'visitors,pageviews',
    ])->throw()->json();

Run that in tinker before you touch the widget. Ninety percent of "the widget is broken" reports end here.

env() inside the config file is fine; env() outside it is not. If you call env('PLAUSIBLE_API_KEY') anywhere in your own code, php artisan config:cache in production will silently return null. Reference config('filament-plausible-widget.api_key') instead.

Self-hosted base URL: no trailing slash. https://analytics.example.com/ produces double-slash paths that some reverse proxies reject. Also confirm your instance actually exposes /api/v1/stats/* — older self-hosted images predate the Stats API.

Timezone drift. Plausible aggregates in the site's configured timezone, not your app's. A "today" card can look wrong by several hours if your team is in a different region. Either align the Plausible site timezone with your app, or avoid labelling anything "today".

cache:clear after changing credentials. The widget's data is cached; rotating an API key and seeing stale/empty numbers sends people down the wrong path.

Version pinning — and what v3.2.0 actually tells you. This is a small, zero-dependents package, so treat it as a readable library rather than infrastructure. Pin the version in composer.json, but note that v3.2.0 (Laravel 12 support, via PR #12) is a compatibility release: it signals the maintainer and community are still tracking new Laravel majors, which is the thing you care about most for a tiny dependency. It adds no new features, no config changes, and nothing to migrate. When the next Laravel major lands, check for a matching bump here before upgrading your app — that release will likely be the same shape as this one. Because it's MIT you can always vendor the widget class into app/Filament/Widgets/ if upstream goes quiet; it's a thin wrapper over one HTTP call, so forking costs an afternoon, not a sprint.

Filament major-version compatibility is the real upgrade risk. Laravel 12 support says nothing about Filament 4. Before bumping Filament, check whether this package's widget still extends the current base class — a stale widget will fail at panel boot with a confusing "class not found" or method-signature error rather than a clear incompatibility message. The v3.2.0 release is a good reminder to read the changelog for what it doesn't say (here: no Filament bump), not just what it does.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib