Stat Plus Easy plugin screenshot
Dark mode ready
Multilingual support
Supports v5.x

Stat Plus Easy

Community

The Filament v3, v4 and v5 Stat with the look every dashboard ends up asking for: icon in the top-right corner, colored accent border on the left and an animated counter — plus a loading skeleton shaped like the real card.

Tags: Widget
Supported versions:
5.x 4.x 3.x
Guilherme Ferro avatar Author: Guilherme Ferro

Package health

Beta

Automated checks of this plugin's Composer package

89 / 100
Security 85
Maintenance 90
Ecosystem 100
15 checks
Third-party plugin. This is built by the community, not the Filament team. Filament does not review, endorse, or vet the security of plugins outside the filament/ namespace. Review the source and install at your own risk. Found malware or an unresolved security issue the author won't address? Report it .
Powered by Plumb Last scanned 11 hours ago

Documentation

filament-stat-plus-easy

Latest Version Total Downloads License

🇺🇸 English · 🇧🇷 Português

The Filament v3, v4 and v5 Stat with the look every dashboard ends up asking for: icon in the top-right corner, colored accent border on the left and an animated counter — plus a loading skeleton shaped like the real card.

Swap Stat::make(...) for StatPlus::make(...), add ->icon(...), done. No published views, no CSS in your theme.

use Gsferro\FilamentStatPlusEasy\Widgets\StatPlus;

protected function getStats(): array
{
    return [
        StatPlus::make('Active projects', Project::active()->count())
            ->icon('heroicon-o-folder-open'),

        StatPlus::make('Breached SLA', Ticket::late()->count())
            ->icon('heroicon-o-exclamation-triangle')
            ->iconColor('danger'),
    ];
}

#🎬 Demo

StatPlus on a dashboard — corner icon, accent border and animated counter:

StatPlus on a dashboard

Independent colors — icon in one color, border in another (or no border at all):

StatPlus colors

Lazy-loading skeleton — same shape as the real card, so nothing jumps on swap:

StatPlus skeleton

#What you get

Feature What it does
StatPlus Stat with corner icon, accent border and animated counter
HasStatPlus The same accent on any Stat of yours, without the counter
HasStatPlusPlaceholder Loading skeleton shaped like the card, for lazy widgets
FilamentStatPlusEasyPlugin Per-panel defaults (icon color, border on/off)

#Compatibility

Filament Supported
v3 (^3.2)
v4 (^4.0)
v5 (^5.0)

PHP 8.2+. The package never publishes or replaces the native Stat view: it injects classes and CSS custom properties into the root element that the view already prints on all three versions. Upgrading Filament asks nothing of you here.

#Installation

composer require gsferro/filament-stat-plus-easy
php artisan filament:assets

filament:assets copies the package CSS into public/ — that is the step that makes the look show up. Run it on deploy too.

Registering the plugin (optional, only to change defaults):

use Gsferro\FilamentStatPlusEasy\FilamentStatPlusEasyPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugin(FilamentStatPlusEasyPlugin::make());
}

Publishing the config (optional):

php artisan vendor:publish --tag=filament-stat-plus-easy-config

#Usage

#The basics

->icon() is the trigger: with an icon, the card gets the corner box and the left border, both in the stat color.

StatPlus::make('Organizations', 42)
    ->icon('heroicon-o-building-library');

Without ->icon() and without ->accentColor(), StatPlus renders exactly like the native stat. That is what makes incremental migration safe: swap the class today, add the icon whenever you want.

#Colors — icon and border are independent

// both in the icon color
StatPlus::make('Organizations', 42)->icon('heroicon-o-building-library')->iconColor('success');

// icon only, no border
StatPlus::make('Organizations', 42)->icon('heroicon-o-building-library')->accentColor(false);

// icon in one color, border in another
StatPlus::make('SLA', 3)->icon('heroicon-o-clock')->iconColor('gray')->accentColor('danger');

// border only, no icon
StatPlus::make('Revenue', '$1.2M')->accentColor('success');

Icon color resolution:

  1. ->iconColor('success') — always wins
  2. otherwise ->color('danger') (the native method, which also paints description and chart)
  3. otherwise config('filament-stat-plus-easy.icon_color') (default: primary)

Border color resolution:

  1. ->accentColor(false) — no border
  2. ->accentColor('danger') — its own color
  3. otherwise the icon color — and with no icon, there is no border

Colors are names of colors registered in the panel (->colors([...])), not hex values. The CSS reads the scale through the --{color}-500 variable, which is what keeps the card on-theme in light and dark mode. A color passed as an 11-shade array falls back to the default.

#Everything from the native Stat still works

StatPlus::make('Projects', 128)
    ->icon('heroicon-o-folder-open')
    ->description('12% this month')
    ->descriptionIcon('heroicon-m-arrow-trending-up')
    ->chart([7, 12, 9, 14, 18, 22])
    ->url(ProjectResource::getUrl())
    ->extraAttributes(['class' => 'my-class']);

Chart, description, url, polling, extraAttributes — nothing is replaced. What the package adds is appended to what you passed (class and style concatenate, they do not overwrite).

#Animated counter

StatPlus extends OdometerStat from filament-odometer-easy — shipped as a dependency — so the value animates on its own:

StatPlus::make('Revenue', 1250000)
    ->icon('heroicon-o-banknotes')
    ->format(['style' => 'currency', 'currency' => 'USD'])
    ->duration(1500);

Want the accent without the counter? Apply the trait to your own class:

use Filament\Widgets\StatsOverviewWidget\Stat;
use Gsferro\FilamentStatPlusEasy\Concerns\HasStatPlus;

class MyStat extends Stat
{
    use HasStatPlus;
}

#Loading skeleton (lazy widgets)

A lazy widget paints a placeholder until it hydrates. Filament's default is a plain card — when the real widget arrives with icon and border, the layout shifts. The trait fixes that:

use Filament\Widgets\StatsOverviewWidget;
use Gsferro\FilamentStatPlusEasy\Concerns\HasStatPlusPlaceholder;

class OverviewStats extends StatsOverviewWidget
{
    use HasStatPlusPlaceholder;

    protected static bool $isLazy = true;
}

By default the skeleton draws 4 cards, all with the accent, and does not run getStats() — the placeholder runs before hydration, and calling the stats there would fire the very queries lazy loading defers.

Optional tweaks:

// a different number of cards
protected function getPlaceholderStatsCount(): int
{
    return 3;
}

// mixed widget (some stats with accent, some without): read the real stats and
// mirror them card by card — costs the queries in the placeholder
protected bool $placeholderReadsStats = true;

The skeleton uses the same classes as the real card (fi-wi-stats-overview-stat, fi-stat-plus, fi-stat-plus-bordered), only in gray — that is what guarantees the swap does not move anything. It also respects the widget columnSpan.

#Configuration

#Fluent, per panel

->plugin(
    FilamentStatPlusEasyPlugin::make()
        ->iconColor('info')   // default icon color when the stat defines none
        ->accent(false)       // border off by default in this panel
)

#Or through the config file

// config/filament-stat-plus-easy.php
return [
    'icon_color' => 'primary',
    'accent' => true,
];

#Sizing, through CSS

Icon size, radius, offset, border width and background opacity are custom properties with defaults. Override wherever it makes sense — in your theme (global), per panel, or on a single card:

/* in your theme.css */
.fi-wi-stats-overview-stat.fi-stat-plus {
    --stat-plus-icon-size: 3rem;
    --stat-plus-icon-radius: 9999px;   /* round icon */
    --stat-plus-border-width: 6px;
}
// or on this card only
StatPlus::make('Projects', 12)
    ->icon('heroicon-o-folder-open')
    ->extraAttributes(['style' => '--stat-plus-border-width: 8px']);
Custom property Default What it controls
--stat-plus-icon-size 2.5rem Icon box side
--stat-plus-icon-padding 0.5rem Breathing room between box and glyph
--stat-plus-icon-offset 1.25rem Distance from the card corner
--stat-plus-icon-radius 0.625rem Icon box corner radius
--stat-plus-icon-opacity 12% Icon box background opacity (light)
--stat-plus-icon-opacity-dark 22% Icon box background opacity (dark)
--stat-plus-border-width 4px Accent border thickness

#How it works under the hood

  • No published views. The native Stat view already prints $getExtraAttributeBag() on the root element in v3, v4 and v5. The package injects two classes (fi-stat-plus, fi-stat-plus-bordered) and two custom properties (--stat-plus-icon, --stat-plus-accent) there. CSS does the rest: the card is already relative in every version, so the icon only needs positioning.
  • Static CSS, registered as an asset. No @apply, no Tailwind utilities, no @layer — the file does not go through the consuming app's build. That is why the package needs no theme edits; filament:assets copies it and Filament injects it into every panel.
  • One real difference between versions: the format of the color variables. v3 stores RGB channels (--primary-500: 217 119 6), v4/v5 store a ready color (oklch(...)). PHP resolves it, writing either rgb(var(--primary-500)) or var(--primary-500) depending on the installed version. The CSS stays the same for everyone.
  • The icon markup changed between versions (.fi-wi-stats-overview-stat-icon in v3, .fi-icon inside .fi-wi-stats-overview-stat-label-ctn in v4/v5), so the rule covers both selectors. Neither one reaches the description icon.
  • Color names are validated before entering the style string (Filament does not escape extra attribute values): only [a-z0-9-] slugs pass, anything else falls back to the default. This matters when the color comes from data, such as per-tenant branding.

#Development

composer install
composer test        # Pest
composer analyse     # PHPStan
composer lint        # Pint

The CSS is plain CSS committed at resources/dist/filament-stat-plus-easy.cssthere is no build step. Edit, test, commit.

The README GIFs come from art/demo/index.html (the page loads the real package CSS) via bash art/make-gifs.sh — headless Chrome for the frames, ffmpeg to assemble them. Each frame is a screenshot with a different ?t=, so capture is deterministic. The plugin-page cover (art/thumbnail.jpg, 1280x720) comes from the same page via bash art/make-thumbnail.sh.

#Testing

composer test

#Changelog

See the CHANGELOG.

#Contributing

See CONTRIBUTING.

#Security Vulnerabilities

See the security policy.

#Credits

#License

MIT. See the license file.

The author

Guilherme Ferro avatar Author: Guilherme Ferro

I am passionate about simplifying complex problems, enabling you to leverage solutions efficiently and intuitively. By using my solutions, you will reduce the time and effort required for implementation, making the development process easier and more straightforward.

Plugins
2
Stars
2

From the same author