
widgets
Create FilamentPHP v4 dashboard widgets - stats overviews, charts, and custom components
- Installs
- 75
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/mwguerra/claude-code-plugins --skill widgets- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads widgets automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
FilamentPHP Widgets Generation Skill
Overview
This skill generates FilamentPHP v4 dashboard widgets including stats overview widgets, chart widgets, table widgets, and custom widgets.
Documentation Reference
CRITICAL: Before generating widgets, read:
/home/mwguerra/projects/mwguerra/claude-code-plugins/filament-specialist/skills/docs/references/widgets/
Creating Widgets
Generate with Artisan
# Basic widget
php artisan make:filament-widget StatsOverview
# Stats overview widget
php artisan make:filament-widget StatsOverview --stats-overview
# Chart widget
php artisan make:filament-widget RevenueChart --chart
# Table widget
php artisan make:filament-widget LatestOrders --table
# Resource widget
php artisan make:filament-widget PostStats --resource=PostResource
Stats Overview Widget
<?php
declare(strict_types=1);
namespace App\Filament\Widgets;
use App\Models\Order;
use App\Models\User;
use Filament\Widgets\StatsOverviewWidget as BaseWidget;
use Filament\Widgets\StatsOverviewWidget\Stat;
class StatsOverview extends BaseWidget
{
protected static ?int $sort = 1;
protected function getStats(): array
{
return [
// Basic stat
Stat::make('Total Users', User::count())
->description('All registered users')
->descriptionIcon('heroicon-o-users')
->color('primary'),
// Stat with trend
Stat::make('New Users', User::whereMonth('created_at', now()->month)->count())
->description('32% increase')
->descriptionIcon('heroicon-m-arrow-trending-up')
->color('success')
->chart([7, 3, 4, 5, 6, 3, 5, 8]) // Sparkline data
->chartColor('success'),
// Stat with decrease trend
Stat::make('Bounce Rate', '21%')
->description('7% decrease')
->descriptionIcon('heroicon-m-arrow-trending-down')
->color('danger'),
// Revenue stat with formatting
Stat::make('Revenue', '$' . number_format(Order::sum('total'), 2))
->description('This month')
->descriptionIcon('heroicon-o-currency-dollar')
->color('success')
->chart([1200, 1400, 1100, 1800, 2200, 1900, 2400])
->chartColor('success'),
// Stat with extra info
Stat::make('Pending Orders', Order::where('status', 'pending')->count())
->description('Requires attention')
->descriptionIcon('heroicon-o-clock')
->color('warning')
->extraAttributes([
'class' => 'cursor-pointer',
'wire:click' => 'goToOrders',
]),
];
}
// Optional: Make stats live
protected static ?string $pollingInterval = '15s';
// Optional: Column span
protected int | string | array $columnSpan = 'full';
}
Chart Widgets
Line Chart
<?php
declare(strict_types=1);
namespace App\Filament\Widgets;
use App\Models\Order;
use Filament\Widgets\ChartWidget;
use Illuminate\Support\Carbon;
class RevenueChart extends ChartWidget
{
protected static ?string $heading = 'Revenue';
protected static ?int $sort = 2;
protected int | string | array $columnSpan = 'full';
protected function getData(): array
{
$data = collect(range(1, 12))->map(function ($month) {
return Order::whereMonth('created_at', $month)
->whereYear('created_at', now()->year)
->sum('total');
});
return [
'datasets' => [
[
'label' => 'Revenue',
'data' => $data->values()->toArray(),
'borderColor' => '#10b981',
'backgroundColor' => 'rgba(16, 185, 129, 0.1)',
'fill' => true,
],
],
'labels' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
];
}
protected function getType(): string
{
return 'line';
}
protected function getOptions(): array
{
return [
'plugins' => [
'legend' => [
'display' => false,
],
],
'scales' => [
'y' => [
'beginAtZero' => true,
'ticks' => [
'callback' => '(value) => "$" + value.toLocaleString()',
],
],
],
];
}
}
Bar Chart
<?php
declare(strict_types=1);
namespace App\Filament\Widgets;
use App\Models\Order;
use Filament\Widgets\ChartWidget;
class OrdersPerCategory extends ChartWidget
{
protected static ?string $heading = 'Orders by Category';
protected static ?int $sort = 3;
protected function getData(): array
{
$categories = \App\Models\Category::withCount('orders')->get();
return [
'datasets' => [
[
'label' => 'Orders',
'data' => $categories->pluck('orders_count')->toArray(),
'backgroundColor' => [
'#3b82f6',
'#10b981',
'#f59e0b',
'#ef4444',
'#8b5cf6',
],
],
],
'labels' => $categories->pluck('name')->toArray(),
];
}
protected function getType(): string
{
return 'bar';
}
}
Doughnut/Pie Chart
<?php
declare(strict_types=1);
namespace App\Filament\Widgets;
use App\Models\Order;
use Filament\Widgets\ChartWidget;
class OrderStatusChart extends ChartWidget
{
protected static ?string $heading = 'Order Status Distribution';
protected static ?int $sort = 4;
protected function getData(): array
{
$statuses = Order::selectRaw('status, COUNT(*) as count')
->groupBy('status')
->pluck('count', 'status');
return [
'datasets' => [
[
'data' => $statuses->values()->toArray(),
'backgroundColor' => [
'#f59e0b', // pending - warning
'#3b82f6', // processing - primary
'#10b981', // completed - success
'#ef4444', // cancelled - danger
],
],
],
'labels' => $statuses->keys()->map(fn ($s) => ucfirst($s))->toArray(),
];
}
protected function getType(): string
{
return 'doughnut'; // or 'pie'
}
protected function getOptions(): array
{
return [
'plugins' => [
'legend' => [
'position' => 'bottom',
],
],
];
}
}
Interactive Chart with Filters
<?php
declare(strict_types=1);
namespace App\Filament\Widgets;
use App\Models\Order;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\Select;
use Filament\Widgets\ChartWidget;
class FilterableRevenueChart extends ChartWidget
{
protected static ?string $heading = 'Revenue Over Time';
public ?string $filter = 'week';
protected function getFilters(): ?array
{
return [
'today' => 'Today',
'week' => 'Last 7 days',
'month' => 'This month',
'year' => 'This year',
];
}
protected function getData(): array
{
$data = match ($this->filter) {
'today' => $this->getTodayData(),
'week' => $this->getWeekData(),
'month' => $this->getMonthData(),
'year' => $this->getYearData(),
};
return [
'datasets' => [
[
'label' => 'Revenue',
'data' => $data['values'],
'borderColor' => '#3b82f6',
],
],
'labels' => $data['labels'],
];
}
protected function getType(): string
{
return 'line';
}
private function getTodayData(): array
{
// Implementation
}
private function getWeekData(): array
{
// Implementation
}
private function getMonthData(): array
{
// Implementation
}
private function getYearData(): array
{
// Implementation
}
}
Table Widget
<?php
declare(strict_types=1);
namespace App\Filament\Widgets;
use App\Models\Order;
use Filament\Tables;
use Filament\Tables\Table;
use Filament\Widgets\TableWidget as BaseWidget;
class LatestOrders extends BaseWidget
{
protected static ?string $heading = 'Latest Orders';
protected static ?int $sort = 5;
protected int | string | array $columnSpan = 'full';
public function table(Table $table): Table
{
return $table
->query(
Order::query()
->latest()
->limit(10)
)
->columns([
Tables\Columns\TextColumn::make('number')
->searchable(),
Tables\Columns\TextColumn::make('customer.name')
->label('Customer')
->searchable(),
Tables\Columns\BadgeColumn::make('status')
->colors([
'warning' => 'pending',
'primary' => 'processing',
'success' => 'completed',
'danger' => 'cancelled',
]),
Tables\Columns\TextColumn::make('total')
->money('usd')
->sortable(),
Tables\Columns\TextColumn::make('created_at')
->dateTime()
->sortable(),
])
->actions([
Tables\Actions\Action::make('view')
->url(fn (Order $record): string => route('filament.admin.resources.orders.view', $record))
->icon('heroicon-o-eye'),
])
->paginated(false);
}
}
Custom Widget
<?php
declare(strict_types=1);
namespace App\Filament\Widgets;
use App\Models\Task;
use Filament\Widgets\Widget;
class TasksWidget extends Widget
{
protected static string $view = 'filament.widgets.tasks-widget';
protected static ?int $sort = 6;
protected int | string | array $columnSpan = 1;
public array $tasks = [];
public function mount(): void
{
$this->tasks = Task::where('user_id', auth()->id())
->whereNull('completed_at')
->orderBy('due_date')
->limit(5)
->get()
->toArray();
}
public function completeTask(int $taskId): void
{
Task::find($taskId)->update(['completed_at' => now()]);
$this->mount(); // Refresh tasks
}
}
Blade view (resources/views/filament/widgets/tasks-widget.blade.php):
<x-filament-widgets::widget>
<x-filament::section>
<x-slot name="heading">
My Tasks
</x-slot>
<ul class="divide-y divide-gray-200 dark:divide-gray-700">
@forelse ($tasks as $task)
<li class="py-3 flex items-center justify-between">
<div>
<p class="text-sm font-medium text-gray-900 dark:text-white">
{{ $task['title'] }}
</p>
<p class="text-xs text-gray-500">
Due: {{ \Carbon\Carbon::parse($task['due_date'])->format('M j, Y') }}
</p>
</div>
<x-filament::icon-button
icon="heroicon-o-check"
wire:click="completeTask({{ $task['id'] }})"
color="success"
/>
</li>
@empty
<li class="py-3 text-sm text-gray-500">
No pending tasks
</li>
@endforelse
</ul>
</x-filament::section>
</x-filament-widgets::widget>
Widget Registration
Dashboard Widgets
// In AdminPanelProvider.php
->widgets([
Widgets\AccountWidget::class, // Default
Widgets\FilamentInfoWidget::class, // Default
\App\Filament\Widgets\StatsOverview::class,
\App\Filament\Widgets\RevenueChart::class,
\App\Filament\Widgets\LatestOrders::class,
])
Resource Page Widgets
// In resource class
public static function getWidgets(): array
{
return [
Widgets\PostStatsOverview::class,
];
}
// In ListRecords page
protected function getHeaderWidgets(): array
{
return [
Widgets\PostStatsOverview::class,
];
}
protected function getFooterWidgets(): array
{
return [
Widgets\RecentPosts::class,
];
}
Widget Configuration
class MyWidget extends Widget
{
// Sort order
protected static ?int $sort = 1;
// Column span (1, 2, 'full', or responsive array)
protected int | string | array $columnSpan = [
'md' => 2,
'xl' => 3,
];
// Polling interval
protected static ?string $pollingInterval = '10s';
// Visibility
public static function canView(): bool
{
return auth()->user()->isAdmin();
}
// Lazy loading
protected static bool $isLazy = true;
}
Output
Generated widgets include:
- Proper widget type selection
- Data fetching methods
- Chart configuration
- Styling and layout
- Interactivity (filters, actions)
- Performance optimizations
FAQ
What does the widgets skill do?
Create FilamentPHP v4 dashboard widgets - stats overviews, charts, and custom components
How do I install widgets?
Run npx skills add https://github.com/mwguerra/claude-code-plugins --skill widgets in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/widgets/ (personal) or .claude/skills/widgets/ (project).
Which AI agents can use widgets?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is widgets safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains widgets?
It is published by mwguerra in mwguerra/claude-code-plugins. It has 75 recorded installs.
More from mwguerra/claude-code-plugins
filament-resource
mwguerra/claude-code-plugins
Generate FilamentPHP v4 resources with form, table, relation managers, and actions
forms
mwguerra/claude-code-plugins
Create FilamentPHP v4 forms with fields, validation, sections, tabs, and relationships
dashboard
mwguerra/claude-code-plugins
Open the skill to read its SKILL.md instructions.
Popular data & databases skills
supabase-postgres-best-practices
supabase/agent-skills
Postgres best practices maintained by Supabase, for Postgres running anywhere. Load this skill BEFORE writing or changing anything that lives in a Postgres database: creating or altering tables and columns (including choosing column types), schema design, migrations and declarative schema files, RLS policies and the tests that verify them, indexes, triggers, database functions, queues and scheduled jobs (pgcron, pgmq), vector/semantic search (pgvector), and restoring dumps (pgrestore) or importing data. Also load it when diagnosing slow queries, high CPU, timeouts, EXPLAIN plans, connection exhaustion, locking, bloat, or rows visible to the wrong user or tenant. This is not just a performance guide — schema, migration, security, and SQL authoring tasks need these rules too, even for a one-column change or a single query.
prisma-client-api
prisma/skills
Prisma Client API reference covering model queries, filters, operators, and client methods. Use when writing database queries, using CRUD operations, filtering data, or configuring Prisma Client. Triggers on "prisma query", "findMany", "create", "update", "delete", "$transaction".
prisma-database-setup
prisma/skills
Deprecated compatibility name for prisma-orm-setup. Use only when a user or existing integration explicitly invokes prisma-database-setup; otherwise select prisma-orm-setup.
prisma-cli
prisma/skills
Prisma ORM 7 CLI reference for existing Prisma 7 applications or explicitly selected Prisma 7 workflows. Covers prisma init, generate, migrate, db, dev, studio, and mcp. For setup, connection repair, or Prisma 8, use prisma-orm-setup; not the Prisma Platform CLI.
prisma-postgres
prisma/skills
Deprecated compatibility name for prisma-postgres-setup. Use only when a user or existing integration explicitly invokes prisma-postgres; otherwise select prisma-postgres-setup.
prisma-driver-adapter-implementation
prisma/skills
Required reference for Prisma ORM 7 SQL driver adapter work. Use when implementing or modifying adapters, adding database drivers, or touching SqlDriverAdapter, Transaction, savepoint, result mapping, or DriverAdapterError behavior. Covers current transaction lifecycle, optional savepoint hooks, original database-error preservation, and verification.