laboiteacode/filament-dashboard-widgets
A collection of fifteen polished, ready-to-use dashboard widgets for Filament: KPI metrics, goals, breakdowns, trends, funnels, timelines and more. Typed fluent API, static or computed data, native Filament components, light/dark themes, accessible and escaped by default with no front-end build step...
composer require laboiteacode/filament-dashboard-widgets — check the constraint against your Filament major version before committing (this package targets the current Filament widget base classes; a mismatch will fail at container boot, not at install). Pin ^1.0 and commit composer.lock.php artisan filament:assets after install (and in your deploy pipeline). Since v1.0.1 the stylesheet is published as a browser-cached Filament asset; if it is not published the package inlines a fallback so it still works out of the box, but you lose the caching.src/Widgets/ in vendor/ — with 15 widgets and a "typed API", the class signatures are the real documentation. Skim the test suite (tests/) for the canonical constructor/getData() calls; it is usually more accurate than prose for a young package.PanelProvider:->widgets([
\LaBoiteACode\FilamentDashboardWidgets\Widgets\MetricWidget::class,
\LaBoiteACode\FilamentDashboardWidgets\Widgets\GoalWidget::class,
])
StatsOverviewWidget KPI row with the package's metric + goal widgets. As of v1.0.1 the cards use native Filament surface/ring/shadow/type scale, so the comparison is now purely about what the widget shows, not how it looks — which is the right basis for keeping or dropping a dependency. About 20 minutes.->plugin(...)) for config or asset publishing — check whether the package ships a Plugin class before wiring it manually.Panel-level vs. resource-level. Use panel-level registration for global KPIs (MRR, active users, churn). For per-record dashboards, return widgets from getHeaderWidgets() / getFooterWidgets() on a Resource\Pages\ListRecords or a custom Page — this keeps the same widget class reusable across contexts because most of these widgets are configured via static properties, not constructor args.
Centralize configuration in subclasses. With 15 widget types and a consistent API, the dominant pattern is thin subclasses:
class RevenueMetric extends MetricWidget
{
protected static ?string $heading = 'MRR';
protected static int|string|array $columnSpan = ['default' => 1, 'md' => 1, 'xl' => 1];
}
That keeps panel providers readable and gives you a single place to patch when the upstream API changes — which matters given the adoption level.
Keep the query inside your code. Do not let a widget infer scope from the model. Wrap data access behind a service or query object and have the widget call it, so tenant scoping, withoutGlobalScopes, and permission checks live in one auditable place rather than inside vendor configuration callbacks.
Mix with native widgets deliberately. v1.0.1 narrowed the gap further: the package's cards now match Filament's native surface, ring, shadow, type scale and focus ring, and hover-lift on clickable cards is gone. So visual polish is no longer a reason to pick this over StatsOverviewWidget — use the package for the shapes Filament doesn't ship (funnel, bullet, variance, segment bar, usage, timeline) and use core widgets for anything you need to debug frequently. This minimizes your exposure to the package's churn.
Let it format numbers. Number and percentage rendering follows the application locale (1 234,5 / 25 % in fr). Do not pre-format values with number_format() inside getData() or bake units into $heading — you will double-format and produce 25 % %, and you will hard-code the wrong separator for non-English panels.
Live data. If a widget polls (protected static ?string $pollingInterval), pair it with a cached aggregate (Cache::remember, 60s) rather than a raw count()/sum() on every tick. Five polling widgets on a busy dashboard will otherwise dominate query time.
Recent items / detail lists are the highest-leverage widgets in day-to-day use: they turn a static dashboard into a triage surface. Wire row URLs to your resource view/edit pages so clicking through lands in the admin panel rather than a dead end. Since clickable cards no longer lift on hover, make sure the link affordance is obvious in your copy/labels — the focus ring is there for keyboard users, but the mouse cue is subtler now.
Testing. Widgets are Livewire components; Livewire::test(YourWidget::class)->assertOk() plus an assertion on rendered content catches the most common failure (an empty dataset throwing inside a formatting helper) without needing browser tests. If you have locale-sensitive assertions, set the locale explicitly in the test rather than relying on the suite default.
app/Filament/Widgets/ if it goes stale — do that for anything business-critical, and only depend on Packagist for cosmetic widgets."laboiteacode/filament-dashboard-widgets": "^1.0" and commit composer.lock. A Filament v4 upgrade cycle will break widget base classes, and a solo-maintainer package may lag weeks behind.composer.json require block against your target Filament version; the release cadence suggests it tracks closely, but don't assume.color() / badgeColor() values to [a-zA-Z0-9_-], matching Filament's own colour classes. If you pass anything dynamic — a hex, rgb(...), or a slug containing . or / — it will no longer render as before. Normalise to a Filament palette name (success, warning, danger, …) or map untrusted values to a fixed allowlist in your own code. This closes a CSS-declaration injection path, not an XSS hole (Blade escaping already held), so it is a hardening change rather than an incident.$heading or a description; otherwise users will read a rolling 30-day number as "this month". Set the range in app timezone, not UTC, or your "today" boundary will be wrong for non-UTC tenants. Numbers are localised, dates are up to you.['default' => ..., 'md' => ..., 'xl' => ...]. A widget that looks right on desktop can collapse badly on tablet; test with the panel's responsive preview.protected static ?int $sort and remember panel-level ->widgets([...]) order and $sort can disagree. Pick one.php artisan filament:clear-cached-components (and optimize:clear in prod) if a new widget doesn't appear. In production, run filament:cache-components and filament:assets as part of deploy.How can I help you explore Laravel packages today?