widgets

FilamentPHP Widgets Generation Skill

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "widgets" with this command: npx skills add mwguerra/claude-code-plugins/mwguerra-claude-code-plugins-widgets

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>

    &#x3C;ul class="divide-y divide-gray-200 dark:divide-gray-700">
        @forelse ($tasks as $task)
            &#x3C;li class="py-3 flex items-center justify-between">
                &#x3C;div>
                    &#x3C;p class="text-sm font-medium text-gray-900 dark:text-white">
                        {{ $task['title'] }}
                    &#x3C;/p>
                    &#x3C;p class="text-xs text-gray-500">
                        Due: {{ \Carbon\Carbon::parse($task['due_date'])->format('M j, Y') }}
                    &#x3C;/p>
                &#x3C;/div>
                &#x3C;x-filament::icon-button
                    icon="heroicon-o-check"
                    wire:click="completeTask({{ $task['id'] }})"
                    color="success"
                />
            &#x3C;/li>
        @empty
            &#x3C;li class="py-3 text-sm text-gray-500">
                No pending tasks
            &#x3C;/li>
        @endforelse
    &#x3C;/ul>
&#x3C;/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

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

filament-resource

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

resource

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

forms

No summary provided by upstream source.

Repository SourceNeeds Review