widgets Documentation

[READ ONLY] Subtree split of Filament Widgets (see filamentphp/filament)

13 stars PHP PHP 4 files ยท ~15,042 tokens
RAW Doc

Technical Documentation: filamentphp/widgets

โ„น๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference

01 Overview (`docs/01-overview.md`)


title: Overview

Introduction

Filament allows you to build dynamic dashboards, comprised of "widgets". Each widget is an element on the dashboard that displays data in a specific way. For example, you can display stats, chart, or a table.

Creating a widget

To create a widget, you can use the make:filament-widget command:

bash
php artisan make:filament-widget MyWidget

This command will ask you which type of widget you want to create. You can choose from the following options:

  • Custom: A custom widget that you can build from scratch.
  • Chart: A widget that displays a chart.
  • Stats overview: A widget that displays statistics.
  • Table: A widget that displays a table.

Sorting widgets

Each widget class contains a $sort property that may be used to change its order on the page, relative to other widgets:

php
protected static ?int $sort = 2;

Customizing the dashboard page

If you want to customize the dashboard class, for example, to change the number of widget columns, create a new file at app/Filament/Pages/Dashboard.php:

php
<?php

namespace App\Filament\Pages;

use Filament\Pages\Dashboard as BaseDashboard;

class Dashboard extends BaseDashboard
{
    // ...
}

Finally, remove the original Dashboard class from configuration file:

php
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
        ->pages([]);
}

If you don't discover pages with discoverPages() in the directory you created the new dashboard class, you should manually register the class in the pages() method:

php
use App\Filament\Pages\Dashboard;
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->pages([
            Dashboard::class,
        ]);
}

Creating multiple dashboards

If you want to create multiple dashboards, you can do so by repeating the process described above. Creating new pages that extend the Dashboard class will allow you to create as many dashboards as you need.

You will also need to define the URL path to the extra dashboard, otherwise it will be at /:

php
protected static string $routePath = 'finance';

You may also customize the title of the dashboard by overriding the $title property:

php
protected static ?string $title = 'Finance dashboard';

The primary dashboard shown to a user is the first one they have access to (controlled by canAccess() method), according to the defined navigation sort order.

The default sort order for dashboards is -2. You can control the sort order of custom dashboards with $navigationSort:

php
protected static ?int $navigationSort = 15;

Customizing the widgets' grid

You may change how many grid columns are used to display widgets.

Firstly, you must replace the original Dashboard page.

Now, in your new app/Filament/Pages/Dashboard.php file, you may override the getColumns() method to return a number of grid columns to use:

php
public function getColumns(): int | array
{
    return 2;
}

Responsive widgets grid

You may wish to change the number of widget grid columns based on the responsive breakpoint of the browser. You can do this using an array that contains the number of columns that should be used at each breakpoint:

php
public function getColumns(): int | array
{
    return [
        'md' => 4,
        'xl' => 5,
    ];
}

This pairs well with responsive widget widths.

Customizing widget width

You may customize the width of a widget using the $columnSpan property. You may use a number between 1 and 12 to indicate how many columns the widget should span, or full to make it occupy the full width of the page:

php
protected int | string | array $columnSpan = 'full';

Responsive widget widths

You may wish to change the widget width based on the responsive breakpoint of the browser. You can do this using an array that contains the number of columns that the widget should occupy at each breakpoint:

php
protected int | string | array $columnSpan = [
    'md' => 2,
    'xl' => 3,
];

This is especially useful when using a responsive widgets grid.

Conditionally hiding widgets

You may override the static canView() method on widgets to conditionally hide them:

php
public static function canView(): bool
{
    return auth()->user()->isAdmin();
}

Filament also checks canView() before a widget's mount() method runs, including when a lazy widget loads, and before subsequent Livewire updates. If access is denied, the request returns a 403 response. Dashboard and resource pages filter out denied widgets before mounting them.

Keep sensitive work out of the widget's boot() and lazy placeholder() methods, which can run before this authorization check. If you need sensitive work in these methods, authorize it separately.

Table widgets

You may easily add tables to your dashboard. Start by creating a widget with the command:

bash
php artisan make:filament-widget LatestOrders --table

You may now customize the table by editing the widget file.

Custom widgets

To get started building a BlogPostsOverview widget:

bash
php artisan make:filament-widget BlogPostsOverview

This command will create two files - a widget class in the /Widgets directory of the Filament directory, and a view in the /widgets directory of the Filament views directory.

The class is a Livewire component, so any Livewire features are available to you. The Blade view can contain any HTML you like, and you can access any public Livewire properties in the view. You can also access the Livewire component instance in the view using $this.

You can also render widgets with React, Vue, Svelte, or framework-free JavaScript. Add --react, --vue, --svelte, or --js to the command to generate a renderer instead of a Blade view. See custom widgets for the renderer lifecycle, PHP props, and dashboard filter synchronization.

Filtering widget data

You may add a form to the dashboard that allows the user to filter the data displayed across all widgets. When the filters are updated, the widgets will be reloaded with the new data.

Firstly, you must replace the original Dashboard page.

Now, in your new app/Filament/Pages/Dashboard.php file, you may add the HasFiltersForm trait, and add the filtersForm() method to return form components:

php
use Filament\Forms\Components\DatePicker;
use Filament\Pages\Dashboard as BaseDashboard;
use Filament\Pages\Dashboard\Concerns\HasFiltersForm;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;

class Dashboard extends BaseDashboard
{
    use HasFiltersForm;

    public function filtersForm(Schema $schema): Schema
    {
        return $schema
            ->components([
                Section::make()
                    ->schema([
                        DatePicker::make('startDate'),
                        DatePicker::make('endDate'),
                        // ...
                    ])
                    ->columns(3),
            ]);
    }
}

In widget classes that require data from the filters, you need to add the InteractsWithPageFilters trait, which will allow you to use the $this->pageFilters property to access the raw data from the filters form:

php
use App\Models\BlogPost;
use Carbon\CarbonImmutable;
use Filament\Widgets\StatsOverviewWidget;
use Filament\Widgets\Concerns\InteractsWithPageFilters;
use Illuminate\Database\Eloquent\Builder;

class BlogPostsOverview extends StatsOverviewWidget
{
    use InteractsWithPageFilters;

    public function getStats(): array
    {
        $startDate = $this->pageFilters['startDate'] ?? null;
        $endDate = $this->pageFilters['endDate'] ?? null;

        return [
            StatsOverviewWidget\Stat::make(
                label: 'Total posts',
                value: BlogPost::query()
                    ->when($startDate, fn (Builder $query) => $query->whereDate('created_at', '>=', $startDate))
                    ->when($endDate, fn (Builder $query) => $query->whereDate('created_at', '<=', $endDate))
                    ->count(),
            ),
            // ...
        ];
    }
}

The $this->pageFilters array will always reflect the current form data. Please note that this data is not validated, as it is available live and not intended to be used for anything other than querying the database. You must ensure that the data is valid before using it. In this example, we check if the start date is set before using it in the query.

Filtering widget data using an action modal

Alternatively, you can swap out the filters form for an action modal, that can be opened by clicking a button in the header of the page. There are many benefits to using this approach:

  • The filters form is not always visible, which allows you to use the full height of the page for widgets.
  • The filters do not update the widgets until the user clicks the "Apply" button, which means that the widgets are not reloaded until the user is ready. This can improve performance if the widgets are expensive to load.
  • Validation can be performed on the filters form, which means that the widgets can rely on the fact that the data is valid - the user cannot submit the form until it is. Canceling the modal will discard the user's changes.

To use an action modal instead of a filters form, you can use the HasFiltersAction trait instead of HasFiltersForm. Then, register the FilterAction class as an action in getHeaderActions():

php
use Filament\Forms\Components\DatePicker;
use Filament\Pages\Dashboard as BaseDashboard;
use Filament\Pages\Dashboard\Actions\FilterAction;
use Filament\Pages\Dashboard\Concerns\HasFiltersAction;

class Dashboard extends BaseDashboard
{
    use HasFiltersAction;
    
    protected function getHeaderActions(): array
    {
        return [
            FilterAction::make()
                ->schema([
                    DatePicker::make('startDate'),
                    DatePicker::make('endDate'),
                    // ...
                ]),
        ];
    }
}

Handling data from the filter action is the same as handling data from the filters header form, except that the data is validated before being passed to the widget. The InteractsWithPageFilters trait still applies.

Persisting widget filters in the user's session

By default, the dashboard filters applied will persist in the user's session between page loads. If you use tenancy, filters are persisted separately for each tenant. To disable persistence, override the $persistsFiltersInSession property in the dashboard page class:

php
use Filament\Pages\Dashboard as BaseDashboard;
use Filament\Pages\Dashboard\Concerns\HasFiltersForm;

class Dashboard extends BaseDashboard
{
    use HasFiltersForm;

    protected bool $persistsFiltersInSession = false;
}

Alternatively, override the persistsFiltersInSession() method in the dashboard page class:

php
use Filament\Pages\Dashboard as BaseDashboard;
use Filament\Pages\Dashboard\Concerns\HasFiltersForm;

class Dashboard extends BaseDashboard
{
    use HasFiltersForm;

    public function persistsFiltersInSession(): bool
    {
        return false;
    }
}

Disabling the default widgets

By default, two widgets are displayed on the dashboard. These widgets can be disabled by updating the widgets() array of the configuration:

php
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->widgets([]);
}

02 Stats Overview (`docs/02-stats-overview.md`)


title: Stats overview widgets

Introduction

Filament comes with a "stats overview" widget template, which you can use to display a number of different stats in a single widget, without needing to write a custom view.

Start by creating a widget with the command:

bash
php artisan make:filament-widget StatsOverview --stats-overview

This command will create a new StatsOverview.php file. Open it, and return Stat instances from the getStats() method:

php
<?php

namespace App\Filament\Widgets;

use Filament\Widgets\StatsOverviewWidget as BaseWidget;
use Filament\Widgets\StatsOverviewWidget\Stat;

class StatsOverview extends BaseWidget
{
    protected function getStats(): array
    {
        return [
            Stat::make('Unique views', '192.1k'),
            Stat::make('Bounce rate', '21%'),
            Stat::make('Average time on page', '3:12'),
        ];
    }
}

Now, check out your widget in the dashboard.

Adding a description and icon to a stat

You may add a description() to provide additional information, along with a descriptionIcon():

php
use Filament\Widgets\StatsOverviewWidget\Stat;

protected function getStats(): array
{
    return [
        Stat::make('Unique views', '192.1k')
            ->description('32k increase')
            ->descriptionIcon('heroicon-m-arrow-trending-up'),
        Stat::make('Bounce rate', '21%')
            ->description('7% decrease')
            ->descriptionIcon('heroicon-m-arrow-trending-down'),
        Stat::make('Average time on page', '3:12')
            ->description('3% increase')
            ->descriptionIcon('heroicon-m-arrow-trending-up'),
    ];
}

The descriptionIcon() method also accepts a second parameter to put the icon before the description instead of after it:

php
use Filament\Support\Enums\IconPosition;
use Filament\Widgets\StatsOverviewWidget\Stat;

Stat::make('Unique views', '192.1k')
    ->description('32k increase')
    ->descriptionIcon('heroicon-m-arrow-trending-up', IconPosition::Before)

Changing the color of the stat

You may also give stats a color:

php
use Filament\Widgets\StatsOverviewWidget\Stat;

protected function getStats(): array
{
    return [
        Stat::make('Unique views', '192.1k')
            ->description('32k increase')
            ->descriptionIcon('heroicon-m-arrow-trending-up')
            ->color('success'),
        Stat::make('Bounce rate', '21%')
            ->description('7% increase')
            ->descriptionIcon('heroicon-m-arrow-trending-down')
            ->color('danger'),
        Stat::make('Average time on page', '3:12')
            ->description('3% increase')
            ->descriptionIcon('heroicon-m-arrow-trending-up')
            ->color('success'),
    ];
}

Adding extra HTML attributes to a stat

You may also pass extra HTML attributes to stats using extraAttributes():

php
use Filament\Widgets\StatsOverviewWidget\Stat;

protected function getStats(): array
{
    return [
        Stat::make('Processed', '192.1k')
            ->color('success')
            ->extraAttributes([
                'class' => 'cursor-pointer',
                'wire:click' => "\$dispatch('setStatusFilter', { filter: 'processed' })",
            ]),
        // ...
    ];
}

In this example, we are deliberately escaping the $ in $dispatch() since this needs to be passed directly to the HTML, it is not a PHP variable.

Setting a placeholder for a stat

Sometimes a stat's value may not be available โ€” for example, when a dashboard has nothing to show for a specific period. You can use placeholder() to define what should be displayed instead when the value is blank:

php
use Filament\Widgets\StatsOverviewWidget\Stat;

Stat::make('Unique views', $uniqueViews)
    ->placeholder('-')

A value is considered blank according to Laravel's blank() helper. For example, null and empty strings are blank, while 0 and '0' are not.

Setting a default placeholder globally

If you'd like every stat in your app to fall back to the same placeholder by default, you can use the static configureUsing() method in a service provider's boot() method:

php
use Filament\Widgets\StatsOverviewWidget\Stat;

Stat::configureUsing(function (Stat $component): void {
    $component->placeholder('-');
});

Individual stats can still override this by calling placeholder() themselves.

Adding a chart to a stat

You may also add or chain a chart() to each stat to provide historical data. The chart() method accepts an array of data points to plot:

php
use Filament\Widgets\StatsOverviewWidget\Stat;

protected function getStats(): array
{
    return [
        Stat::make('Unique views', '192.1k')
            ->description('32k increase')
            ->descriptionIcon('heroicon-m-arrow-trending-up')
            ->chart([7, 2, 10, 3, 15, 4, 17])
            ->color('success'),
        // ...
    ];
}

Styling stat charts in a theme

To style stat charts from a custom theme, set these CSS custom properties on .fi-wi-stats-overview-stat or a parent element:

css
.fi-wi-stats-overview-stat {
    --stat-chart-border-width: 1;
    --stat-chart-line-tension: 0;
    --stat-chart-fill: none;
}
  • --stat-chart-border-width: The line thickness.
  • --stat-chart-line-tension: The curvature, from 0 for straight segments to 1.
  • --stat-chart-fill: The fill target: start, end, origin, or stack. Use none to disable the fill.

Use unitless numbers and the supported keywords, not CSS lengths. You may set different values for light and dark mode.

The chart takes its colors from the color of the stat. To change them in a theme, style the .fi-wi-stats-overview-stat-chart-bg-color and .fi-wi-stats-overview-stat-chart-border-color elements with an ordinary color declaration:

css
.fi-wi-stats-overview-stat {
    & .fi-wi-stats-overview-stat-chart-border-color {
        @apply text-gray-400 dark:text-gray-500;
    }
}

Live updating stats (polling)

By default, stats overview widgets refresh their data every 5 seconds.

To customize this, you may override the $pollingInterval property on the class to a new interval:

php
protected ?string $pollingInterval = '10s';

Alternatively, you may disable polling altogether:

php
protected ?string $pollingInterval = null;

Disabling lazy loading

By default, widgets are lazy-loaded. This means that they will only be loaded when they are visible on the page.

To disable this behavior, you may override the $isLazyLoaded property on the widget class:

php
protected static ?bool $isLazyLoaded = false;

Adding a heading and description

You may also add heading and description text above the widget by overriding the $heading and $description properties:

php
protected ?string $heading = 'Analytics';

protected ?string $description = 'An overview of some analytics.';

If you need to dynamically generate the heading or description text, you can instead override the getHeading() and getDescription() methods:

php
protected function getHeading(): ?string
{
    return 'Analytics';
}

protected function getDescription(): ?string
{
    return 'An overview of some analytics.';
}

03 Charts (`docs/03-charts.md`)


title: Chart widgets

Introduction

Filament comes with many "chart" widget templates, which you can use to display real-time, interactive charts.

Start by creating a widget with the command:

bash
php artisan make:filament-widget BlogPostsChart --chart

There is a single ChartWidget class that is used for all charts. The type of chart is set by the getType() method. In this example, that method returns the string 'line'.

The protected ?string $heading variable is used to set the heading that describes the chart. If you need to set the heading dynamically, you can override the getHeading() method.

The getData() method is used to return an array of datasets and labels. Each dataset is a labeled array of points to plot on the chart, and each label is a string. This structure is identical to the Chart.js library, which Filament uses to render charts. You may use the Chart.js documentation to fully understand the possibilities to return from getData(), based on the chart type.

php
<?php

namespace App\Filament\Widgets;

use Filament\Widgets\ChartWidget;

class BlogPostsChart extends ChartWidget
{
    protected ?string $heading = 'Blog Posts';

    protected function getData(): array
    {
        return [
            'datasets' => [
                [
                    'label' => 'Blog posts created',
                    'data' => [0, 10, 5, 2, 21, 32, 45, 74, 65, 45, 77, 89],
                ],
            ],
            'labels' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
        ];
    }

    protected function getType(): string
    {
        return 'line';
    }
}

Now, check out your widget in the dashboard.

Available chart types

Below is a list of available chart widget classes which you may extend, and their corresponding Chart.js documentation page, for inspiration on what to return from getData():

For example, you could use a bar chart by returning 'bar' from the getType() method:

Here are examples of the other available chart types:

Customizing the chart color

You can customize the color of the chart data by setting the $color property:

php
protected string $color = 'info';

If you're looking to customize the color further, or use multiple colors across multiple datasets, you can still make use of Chart.js's color options in the data:

php
protected function getData(): array
{
    return [
        'datasets' => [
            [
                'label' => 'Blog posts created',
                'data' => [0, 10, 5, 2, 21, 32, 45, 74, 65, 45, 77, 89],
                'backgroundColor' => '#36A2EB',
                'borderColor' => '#9BD0F5',
            ],
        ],
        'labels' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    ];
}

Generating chart data from an Eloquent model

To generate chart data from an Eloquent model, Filament recommends that you install the flowframe/laravel-trend package. You can view the documentation.

Here is an example of generating chart data from a model using the laravel-trend package:

php
use Flowframe\Trend\Trend;
use Flowframe\Trend\TrendValue;

protected function getData(): array
{
    $data = Trend::model(BlogPost::class)
        ->between(
            start: now()->startOfYear(),
            end: now()->endOfYear(),
        )
        ->perMonth()
        ->count();

    return [
        'datasets' => [
            [
                'label' => 'Blog posts',
                'data' => $data->map(fn (TrendValue $value) => $value->aggregate),
            ],
        ],
        'labels' => $data->map(fn (TrendValue $value) => $value->date),
    ];
}

Filtering chart data

Basic Select filter

You can set up chart filters to change the data that is presented. Commonly, this is used to change the time period that chart data is rendered for.

To set a default filter value, set the $filter property:

php
public ?string $filter = 'today';

Then, define the getFilters() method to return an array of values and labels for your filter:

php
protected function getFilters(): ?array
{
    return [
        'today' => 'Today',
        'week' => 'Last week',
        'month' => 'Last month',
        'year' => 'This year',
    ];
}

You can use the active filter value within your getData() method:

php
protected function getData(): array
{
    $activeFilter = $this->filter;

    // ...
}
Technical Documentation: filamentphp/widgets 01 Overview (`docs/01-overview.md`) title: Overview Introduction Creating a widget Sorting widgets Customizing the dashboard page Creating multiple dashboards Customizing the widgets' grid Conditionally hiding widgets Table widgets Custom widgets Filtering widget data Filtering widget data using an action modal Persisting widget filters in the user's session Disabling the default widgets 02 Stats Overview (`docs/02-stats-overview.md`) title: Stats overview widgets Introduction Adding a description and icon to a stat Changing the color of the stat Adding extra HTML attributes to a stat Setting a placeholder for a stat Setting a default placeholder globally Adding a chart to a stat Styling stat charts in a theme Live updating stats (polling) Disabling lazy loading Adding a heading and description 03 Charts (`docs/03-charts.md`) title: Chart widgets Introduction Available chart types Customizing the chart color Generating chart data from an Eloquent model Filtering chart data Basic Select filter Custom filters Empty state Setting the empty state heading Setting the empty state description Setting the empty state icon Adding empty state actions Using a custom empty state view Live updating chart data (polling) Setting a maximum chart height Setting chart configuration options Styling charts in a theme Styling the chart legend Styling chart tooltips Adding a description Disabling lazy loading Making the chart collapsible Providing an accessible alternative Using custom Chart.js plugins Step 1: Install the plugin with NPM Step 2: Create a JavaScript file importing the plugin Step 3: Compile the JavaScript file with Vite Step 4: Register the JavaScript file in Filament 04 Custom Widgets (`docs/04-custom-widgets.md`) title: Custom widgets Introduction Generating a JavaScript widget Passing configuration from PHP Receiving dashboard filter values Writing a renderer Creating a framework-free JavaScript widget Creating a React widget Creating a Vue widget Creating a Svelte widget Calling PHP methods Typing renderers Synchronizing and disposing renderers Customizing the Blade view Building reusable plugin widgets