PHPackages                             asharif88/filament-plotly - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. asharif88/filament-plotly

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

asharif88/filament-plotly
=========================

Inspired by https://filamentphp.com/plugins/leandrocfe-apex-charts &amp; https://filamentphp.com/plugins/elemind-echarts this plugin delivers plotly.js integration for Filament.

1.2.1(2mo ago)71.3k↓23.1%1MITPHPPHP ^8.2CI passing

Since Dec 21Pushed 2mo agoCompare

[ Source](https://github.com/Asharif88/filament-plotly)[ Packagist](https://packagist.org/packages/asharif88/filament-plotly)[ Docs](https://github.com/asharif88/filament-plotly)[ GitHub Sponsors](https://github.com/Asharif88)[ RSS](/packages/asharif88-filament-plotly/feed)WikiDiscussions main Synced today

READMEChangelog (7)Dependencies (16)Versions (14)Used By (0)

Filament Plotly.js Widget
=========================

[](#filament-plotlyjs-widget)

Inspired by [Leandro Ferreira’s Apex Charts plugin](https://filamentphp.com/plugins/leandrocfe-apex-charts) &amp; [Elemind's Echarts plugin](https://filamentphp.com/plugins/elemind-echarts) this plugin delivers plotly.js integration for Filament.

[![Latest Version on Packagist](https://camo.githubusercontent.com/0052e1e0d3cb84144a299930f5c6f906e338ed090aba13fd22f7c04e406c90b3/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6173686172696638382f66696c616d656e742d706c6f746c792e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/asharif88/filament-plotly)[![GitHub Tests Action Status](https://camo.githubusercontent.com/3084d216a7449ac21eb5d3870f104fa3e123a460cdf1500c86de443fa04bdc52/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6173686172696638382f66696c616d656e742d706c6f746c792f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/asharif88/filament-plotly/actions?query=workflow%3Arun-tests+branch%3Amain)[![GitHub Code Style Action Status](https://camo.githubusercontent.com/8a02db02041ce8df2945367c887a7a4fcfac5b96660892ce427789165ef65759/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6173686172696638382f66696c616d656e742d706c6f746c792f6669782d7068702d636f64652d7374796c652d6973737565732e796d6c3f6272616e63683d6d61696e266c6162656c3d636f64652532307374796c65267374796c653d666c61742d737175617265)](https://github.com/asharif88/filament-plotly/actions?query=workflow%3A%22Fix+PHP+code+styling%22+branch%3Amain)[![Total Downloads](https://camo.githubusercontent.com/4342312af6ace6a4a6a1540a4e894a543f0f9575ecf75da2d3816a743b66b36b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6173686172696638382f66696c616d656e742d706c6f746c792e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/asharif88/filament-plotly)

Table of Contents
-----------------

[](#table-of-contents)

- [Installation](#installation)
- [Usage](#usage)
    - [Setting a widget title](#setting-a-widget-title)
    - [Setting a widget subheading](#setting-a-widget-subheading)
    - [Setting a chart id](#setting-a-chart-id)
    - [Making a widget collapsible](#making-a-widget-collapsible)
    - [Setting a widget height](#setting-a-widget-height)
    - [Setting a widget footer](#setting-a-widget-footer)
    - [Header &amp; Footer Actions](#header--footer-actions)
    - [Hiding header content](#hiding-header-content)
    - [Filtering chart data](#filtering-chart-data)
    - [Live updating (polling)](#live-updating-polling)
    - [Defer loading](#defer-loading)
    - [Loading indicator](#loading-indicator)
    - [Chart overlay](#chart-overlay)
    - [Post-init JS hook](#post-init-js-hook)
    - [Dark / light theme sync](#dark--light-theme-sync)
    - [Plotly event listeners](#plotly-event-listeners)
    - [Streaming support](#streaming-support)
    - [Streaming layout patch](#streaming-layout-patch)
- [Changelog](#changelog)
- [Contributing](#contributing)
- [Credits](#credits)
- [License](#license)

Installation
------------

[](#installation)

You can install the package via composer:

```
composer require asharif88/filament-plotly
```

Register the plugin for the Filament Panels you want to use:

```
use Asharif88\FilamentPlotly\FilamentPlotlyPlugin;
public function panel(Panel $panel): Panel
{
    return $panel
        ->plugins([
            FilamentPlotlyPlugin::make()
        ]);
}
```

Usage
-----

[](#usage)

Start by creating a widget with the command:

```
php artisan make:filament-plotly BlogPostsChart
```

The plugin uses the [Plotly.react](https://plotly.com/javascript/plotlyjs-function-reference/#plotlyreact) function to render charts. This function takes in the chart `data`, `layout` and `config` as parameters.

You need to implement the `getChartData()` method to return an array with `data`, `layout` and `config` keys:

```
use Asharif88\FilamentPlotly\Widgets\PlotlyWidget;

class BlogPostsChart extends PlotlyWidget
{
    protected function getChartData(): array
    {
        return [
            'data' => [
                [
                    'x' => ['2025-07-01', '2025-07-02', '2025-07-03', '2025-07-04', '2025-07-05'],
                    'y' => [10, 15, 13, 17, 22],
                    'type' => 'scatter',
                    'mode' => 'lines+markers',
                    'name' => 'Blog Posts',
                ],
            ],
            'layout' => [
                'title' => 'Blog Posts Over Time',
                'xaxis' => [
                    'title' => 'Date',
                ],
                'yaxis' => [
                    'title' => 'Number of Posts',
                ],
            ],
            'config' => [
                'responsive' => true,
            ],
        ];
    }
}
```

Alternatively, you can set the `data`, `layout` and `config` separately by implementing the following methods:

```
protected function getChartData(): array
{
    return [
        [
            'x' => ['2025-07-01', '2025-07-02', '2025-07-03', '2025-07-04', '2025-07-05'],
            'y' => [10, 15, 13, 17, 22],
            'type' => 'scatter',
            'mode' => 'lines+markers',
            'name' => 'Blog Posts',
        ],
    ];
}

protected function getChartLayout(): array
{
    return [
        'title' => 'Blog Posts Over Time',
        'xaxis' => [
            'title' => 'Date',
        ],
        'yaxis' => [
            'title' => 'Number of Posts',
        ],
    ];
}

protected function getChartConfig(): array
{
    return [
        'responsive' => true,
    ];
}
```

Setting a widget title
----------------------

[](#setting-a-widget-title)

You may set a widget title:

```
protected static ?string $heading = 'Blog Posts Chart';
```

Optionally, you can use the `getHeading()` method.

Setting a widget subheading
---------------------------

[](#setting-a-widget-subheading)

You may set a widget subheading:

```
protected static ?string $subheading = 'This is a subheading';
```

Optionally, you can use the `getSubheading()` method.

Adding custom content
---------------------

[](#adding-custom-content)

You can add custom content before chart within the widget container using the `getbeforeContent()` method.

```
public function getBeforeContent(): null|string|Htmlable|View
{
    return '...';
}
```

Setting a chart id
------------------

[](#setting-a-chart-id)

You may set a chart id:

```
protected static string $chartId = 'blogPostsChart';
```

Making a widget collapsible
---------------------------

[](#making-a-widget-collapsible)

You may set a widget to be collapsible:

```
protected static bool $isCollapsible = true;
```

You can also use the `isCollapsible()` method:

```
protected function isCollapsible(): bool
{
    return true;
}
```

Setting a widget height
-----------------------

[](#setting-a-widget-height)

By default, the widget height is set to `300px`. You may set a custom height:

```
protected static ?int $contentHeight = 400; //px
```

Optionally, you can use the `getContentHeight()` method.

```
protected function getContentHeight(): ?int
{
    return 400;
}
```

Setting a widget footer
-----------------------

[](#setting-a-widget-footer)

You may set a widget footer:

```
protected static ?string $footer = 'Lorem Ipsum is simply dummy text of the printing and typesetting industry.';
```

You can also use the `getFooter()` method:

Custom view:

```
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Contracts\View\View;

protected function getFooter(): null|string|Htmlable|View
{
    return view('custom-footer', ['text' => 'Lorem Ipsum is simply dummy text of the printing and typesetting industry.']);
}
```

```

    {{ $text }}

```

Html string:

```
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Contracts\View\View;
protected function getFooter(): null|string|Htmlable|View
{
    return new HtmlString('Lorem Ipsum is simply dummy text of the printing and typesetting industry.');
}
```

Header &amp; Footer Actions
---------------------------

[](#header--footer-actions)

You can register Filament actions to appear in the widget header or footer. Override `getHeaderActions()` / `getFooterActions()` on your widget to return an array of `Filament\Actions\Action` (or `ActionGroup`) instances. These actions are rendered by Filament's actions component and respect alignment settings.

Example — simple header actions:

```
use Filament\Actions\Action;
use Filament\Support\Enums\Alignment;

protected function getHeaderActions(): array
{
    return [
        Action::make('refresh')
            ->label('Refresh')
            ->icon('heroicon-o-refresh')
            ->action('updateOptions')
            ->button(),

        Action::make('download')
            ->label('Download')
            ->url(route('reports.export'))
            ->color('secondary'),
    ];
}

protected function getHeaderActionsAlignment(): ?Alignment
{
    return Alignment::End; // align header actions to the right
}
```

Example — footer actions:

```
use Filament\Actions\Action;
use Filament\Support\Enums\Alignment;

protected function getFooterActions(): array
{
    return [
        Action::make('details')
            ->label('Details')
            ->url(route('reports.details'))
            ->button(),
    ];
}

protected function getFooterActionsAlignment(): ?Alignment
{
    return Alignment::Center; // center footer actions
}
```

Notes:

- You may return `ActionGroup` instances if you need grouped or dropdown actions.
- Header actions are rendered next to any filter controls defined on the widget.
- Footer actions render above the footer content returned by `getFooter()`.

Hiding header content
---------------------

[](#hiding-header-content)

You can hide header content by **NOT** providing these

- $heading
- getHeading()
- $subheading
- getSubheading()
- getOptions()

Filtering chart data
--------------------

[](#filtering-chart-data)

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

### Filter schema

[](#filter-schema)

You may use components from the [Schemas](https://filamentphp.com/docs/4.x/schemas/overview#available-components) to create custom filters. You need to use `HasFiltersSchema` trait and implement the `filtersSchema()` method to define the filter form schema:

```
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Schema;
use Filament\Widgets\ChartWidget\Concerns\HasFiltersSchema;
use Asharif88\FilamentPlotly\Widgets\PlotlyWidget;

class BlogPostsChart extends PlotlyWidget
{
    use HasFiltersSchema;

    public function filtersSchema(Schema $schema): Schema
    {
        return $schema->components([
            TextInput::make('title')
                ->default('Blog Posts Chart'),

            DatePicker::make('date_start')
                ->default('2025-07-01'),

            DatePicker::make('date_end')
                ->default('2025-07-31'),
        ]);
    }

    /**
    * Use this method to update the chart options when the filter form is submitted.
    */
    public function updatedInteractsWithSchemas(string $statePath): void
    {
        $this->updateOptions();
    }
}
```

The data from the custom filter is available in the `$this->filters` array. You can use the active filter values within your `getChartData()` method:

```
protected function getChartData(): array
{
    $title = $this->filters['title'];
    $dateStart = $this->filters['date_start'];
    $dateEnd = $this->filters['date_end'];

    return [
        //chart options
    ];
}
```

### Single select

[](#single-select)

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

```
public ?string $filter = 'today';
```

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

```
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 `getOptions()` method:

```
protected function getOptions(): array
{
    $activeFilter = $this->filter;

    return [
        //chart options
    ];
}
```

Live updating (polling)
-----------------------

[](#live-updating-polling)

By default, chart widgets refresh their data every 5 seconds.

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

```
protected static ?string $pollingInterval = '10s';
```

Alternatively, you may disable polling altogether:

```
protected static ?string $pollingInterval = null;
```

Defer loading
-------------

[](#defer-loading)

This can be helpful when you have slow queries and you don't want to hold up the entire page load:

```
protected static bool $deferLoading = true;

protected function getChartData(): array
{
    //showing a loading indicator immediately after the page load
    if (!$this->readyToLoad) {
        return [];
    }

    //slow query
    sleep(2);

    return [
        //chart options
    ];
}
```

Loading indicator
-----------------

[](#loading-indicator)

You can change the loading indicator:

```
protected static ?string $loadingIndicator = 'Loading...';
```

You can also use the `getLoadingIndicator()` method:

```
use Illuminate\Contracts\View\View;
protected function getLoadingIndicator(): null|string|View
{
    return view('custom-loading-indicator');
}
```

```

    Loading...

```

Chart overlay
-------------

[](#chart-overlay)

Use `getChartOverlay()` to render HTML **inside** the chart container — directly on top of the Plotly canvas. This is the right place for progress bars, custom annotation panels, and loader overlays, because the content sits naturally inside the chart div without requiring `document.getElementById` or absolute-positioning tricks.

```
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Contracts\View\View;

protected function getChartOverlay(): null|string|Htmlable|View
{
    return view('charts.my-overlay');
}
```

```
{{-- resources/views/charts/my-overlay.blade.php --}}

```

> The overlay is rendered inside a `wire:ignore` container, so it is set once on mount and then controlled by JavaScript. It will not be re-rendered by Livewire on subsequent updates.

Post-init JS hook
-----------------

[](#post-init-js-hook)

Override `getOnChartReadyScript()` to run plain JavaScript the moment the Plotly chart is fully initialised. Inside the script, `el` refers to the chart DOM element.

```
protected function getOnChartReadyScript(): ?string
{
    return  '#1e293b',
            'plot_bgcolor'  => '#1e293b',
            'font'          => ['color' => '#f1f5f9'],
        ];
    }

    protected function getLightThemeLayout(): array
    {
        return [
            'paper_bgcolor' => '#ffffff',
            'plot_bgcolor'  => '#ffffff',
            'font'          => ['color' => '#0f172a'],
        ];
    }
}
```

The layout properties returned by each method are merged into the live chart layout via `Plotly.relayout()`. Return any valid [Plotly layout](https://plotly.com/javascript/reference/layout/) keys.

Plotly event listeners
----------------------

[](#plotly-event-listeners)

Override `getPlotlyEventListeners()` to map Plotly JS events to public methods on your widget. When a mapped event fires, two things happen:

1. The mapped method is called on this widget with a serialised payload.
2. A window-level Alpine event `plotly:{event}` is dispatched so that **sibling Livewire components** (a table, a slide-over) can also react without coupling to the chart widget.

```
protected function getPlotlyEventListeners(): array
{
    return [
        'plotly_click'    => 'onChartClick',
        'plotly_selected' => 'onChartSelected',
    ];
}

public function onChartClick(array $data): void
{
    $recordId = $data['points'][0]['customdata'] ?? null;

    $this->dispatch('open-record', id: $recordId);
}

public function onChartSelected(array $data): void
{
    $ids = collect($data['points'])->pluck('customdata')->filter()->all();

    $this->dispatch('filter-table', ids: $ids);
}
```

Store the record's primary key in `customdata` when building trace data — that's the standard Plotly mechanism for carrying arbitrary metadata per point:

```
protected function getChartData(): array
{
    $rows = Order::query()->get();

    return [[
        'x'          => $rows->pluck('created_at'),
        'y'          => $rows->pluck('total'),
        'customdata' => $rows->pluck('id'),   // ← record IDs travel with each point
        'type'       => 'scatter',
        'mode'       => 'markers',
    ]];
}
```

To react from a **separate** Livewire component (e.g. a `ListOrders` resource page), listen for the broadcasted window event:

```
use Livewire\Attributes\On;

#[On('plotly:plotly_click')]
public function handleChartClick(array $data): void
{
    $this->tableFilters['id'] = $data['points'][0]['customdata'];
    $this->resetTable();
}
```

### Supported events and payload shapes

[](#supported-events-and-payload-shapes)

EventPayload fields`plotly_click`, `plotly_hover`, `plotly_unhover`, `plotly_doubleclick``points[]` → `{curveNumber, pointIndex, x, y, z, text, customdata, traceName}``plotly_selected`, `plotly_deselect``points[]` + `range`, `lassoPoints``plotly_legendclick`, `plotly_legenddoubleclick``{curveNumber, traceName}``plotly_relayout`, `plotly_restyle`, `plotly_autosize`raw layout/style change objectDOM nodes, full trace objects, and axis definitions are stripped before serialisation — only safe, JSON-friendly values reach Livewire.

Streaming support
-----------------

[](#streaming-support)

The `HasStreamingSupport` concern replaces the traditional `getFooter()` SSE boilerplate with a small set of override methods. The library owns the `EventSource` lifecycle, the progress overlay, and component cleanup — your widget only declares what is domain-specific.

### SSE message protocol

[](#sse-message-protocol)

Your server-side stream must emit JSON messages in this format:

MessageMeaning`{"init": true, "total": N}`Stream is starting; `N` is the total number of data messages expected (used for the progress bar).`{"done": true}`Stream finished. The library closes the `EventSource` and removes the progress overlay.`{ ...data }`A data point. Forwarded to `getOnStreamMessageScript()`.### Basic example

[](#basic-example)

```
use Asharif88\FilamentPlotly\Concerns\HasStreamingSupport;
use Asharif88\FilamentPlotly\Widgets\PlotlyWidget;

class LiveDataChart extends PlotlyWidget
{
    use HasStreamingSupport;

    public int $sourceId = 1;

    // Return null to disable streaming (e.g. before required state is set)
    protected function getStreamUrl(): ?string
    {
        return url('/stream/data');
    }

    // Query-string params appended to the URL on both initial load and every restart
    protected function getStreamParams(): array
    {
        return ['source_id' => $this->sourceId];
    }

    // JS body called for each data message. `d` = parsed JSON, `el` = chart DOM element.
    // Return null to use the built-in default: Plotly.extendTraces(el, {x:[[d.x]], y:[[d.y]]}, [0])
    protected function getOnStreamMessageScript(): ?string
    {
        return  [], 'y' => [], 'type' => 'scatter', 'mode' => 'lines']];
    }
}
```

> Returning `null` from `getStreamUrl()` disables streaming entirely — useful when required state (e.g. a selected source) has not been set yet.

### Full example with theme and click events

[](#full-example-with-theme-and-click-events)

The following shows `HasStreamingSupport`, `HasChartTheme`, and `getPlotlyEventListeners()` working together, which is the recommended pattern for a production streaming widget:

```
use Asharif88\FilamentPlotly\Concerns\HasChartTheme;
use Asharif88\FilamentPlotly\Concerns\HasStreamingSupport;
use Asharif88\FilamentPlotly\Widgets\PlotlyWidget;
use Livewire\Attributes\On;

class DailyVariationChart extends PlotlyWidget
{
    use HasStreamingSupport;
    use HasChartTheme;

    protected ?string $pollingInterval = null;
    protected static ?string $chartId  = 'dailyVariationChart';
    protected static int $contentHeight = 660;

    public ?int $sourceId  = null;
    public ?string $dateFrom = null;
    public ?string $dateTo   = null;

    // --- Filter handlers -----------------------------------------------------

    #[On('sourceSelected')]
    public function onSourceSelected(int $sourceId): void
    {
        $this->sourceId = $sourceId;
    }

    #[On('dateRangeChanged')]
    public function onDateRangeChanged(?string $from, ?string $to): void
    {
        $this->dateFrom = $from;
        $this->dateTo   = $to;
    }

    // --- Chart data ----------------------------------------------------------

    // Starts empty — data is streamed in via extendTraces
    protected function getChartData(): array
    {
        return [[
            'x'          => [],
            'y'          => [],
            'customdata' => [],
            'mode'       => 'lines+markers',
            'line'       => ['width' => 2, 'color' => 'orange'],
            'marker'     => ['size' => 6, 'color' => 'orange'],
            'showlegend' => false,
        ]];
    }

    protected function getChartLayout(): array
    {
        return [
            'title'      => ['text' => 'Variations per day'],
            'yaxis'      => ['type' => 'category', 'autorange' => 'reversed'],
            'autosize'   => true,
            'showlegend' => false,
        ];
    }

    protected function getChartConfig(): array
    {
        return ['responsive' => true];
    }

    // --- HasStreamingSupport -------------------------------------------------

    protected function getStreamUrl(): ?string
    {
        return $this->sourceId ? url('/stream/data') : null;
    }

    protected function getStreamParams(): array
    {
        return [
            'source_id' => $this->sourceId,
            'date_from' => $this->dateFrom ?? '',
            'date_to'   => $this->dateTo   ?? '',
        ];
    }

    // Expected message shape: { x: , y: , payloadId:  }
    protected function getOnStreamMessageScript(): ?string
    {
        return  '#1e293b',
            'plot_bgcolor'  => '#1e293b',
            'font'          => ['color' => '#f1f5f9'],
        ];
    }

    protected function getLightThemeLayout(): array
    {
        return [
            'paper_bgcolor' => '#ffffff',
            'plot_bgcolor'  => '#ffffff',
            'font'          => ['color' => '#0f172a'],
        ];
    }

    // --- Plotly event listeners ----------------------------------------------

    protected function getPlotlyEventListeners(): array
    {
        return ['plotly_click' => 'onChartClick'];
    }

    public function onChartClick(array $data): void
    {
        $payloadId = $data['points'][0]['customdata'] ?? null;

        if ($payloadId !== null) {
            $this->dispatch('payloadSelected', payloadId: (int) $payloadId);
        }
    }
}
```

Streaming layout patch
----------------------

[](#streaming-layout-patch)

Override `getStreamingLayoutPatch()` to declare layout properties that must be force-merged into the layout on every stream reset, regardless of what `el.layout` currently holds.

```
protected function getStreamingLayoutPatch(): array
{
    return [
        'template' => [
            'layout' => [
                'paper_bgcolor' => '#1e293b',
                'plot_bgcolor'  => '#1e293b',
            ],
        ],
    ];
}
```

> **When to use this vs `HasChartTheme`:** If you use `HasChartTheme`, theme properties are tracked in `el.layout` and are automatically preserved across stream resets — you do not need `getStreamingLayoutPatch()` for theme sync. Use `getStreamingLayoutPatch()` when you need to inject layout properties that are not managed by `HasChartTheme`, or when you cannot use that trait.

Dark mode
---------

[](#dark-mode)

The dark mode is supported and enabled by default for the container.

Publishing views
----------------

[](#publishing-views)

Optionally, you can publish the views using

```
php artisan vendor:publish --tag="filament-plotly-views"
```

Publishing translations
-----------------------

[](#publishing-translations)

Optionally, you can publish the translations using:

```
php artisan vendor:publish --tag=filament-plotly-translations
```

Testing
-------

[](#testing)

```
composer test
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details.

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

Please review [our security policy](.github/SECURITY.md) on how to report security vulnerabilities.

Credits
-------

[](#credits)

- [Ahmad SHARIF](https://github.com/Asharif88)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

47

—

FairBetter than 93% of packages

Maintenance86

Actively maintained with recent releases

Popularity26

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity55

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 80.5% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~13 days

Recently: every ~4 days

Total

10

Last Release

70d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/7bc3352a55b3d4643b40bfa1d42b4a3729f2729300e65e924a2e8b09fd31964f?d=identicon)[asharif88](/maintainers/asharif88)

---

Top Contributors

[![Asharif88](https://avatars.githubusercontent.com/u/1073094?v=4)](https://github.com/Asharif88 "Asharif88 (33 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (6 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (2 commits)")

---

Tags

filament-widgetfilamentphplaravelplotlyplotlyjslaravelfilament-pluginfilamentphpfilament-v5filament-v4plotly.jsFilament Widgetsfilament-plotlyAhmad SHARIFAsharif88

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan, Rector

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/asharif88-filament-plotly/health.svg)

```
[![Health](https://phpackages.com/badges/asharif88-filament-plotly/health.svg)](https://phpackages.com/packages/asharif88-filament-plotly)
```

###  Alternatives

[dotswan/filament-map-picker

Easily pick and retrieve geo-coordinates using a map-based interface in your Filament applications.

128192.3k3](/packages/dotswan-filament-map-picker)[stephenjude/filament-feature-flags

Filament implementation of feature flags and segmentation with Laravel Pennant.

122177.8k1](/packages/stephenjude-filament-feature-flags)[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3914.6k](/packages/rawilk-profile-filament-plugin)[bezhansalleh/filament-google-analytics

Google Analytics integration for FilamentPHP

211189.7k8](/packages/bezhansalleh-filament-google-analytics)[marcelweidum/filament-passkeys

Use passkeys in your filamentphp app

6649.5k1](/packages/marcelweidum-filament-passkeys)[marcelweidum/filament-expiration-notice

Customize the livewire expiration notice

94135.4k5](/packages/marcelweidum-filament-expiration-notice)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
