PHPackages                             happenv-com/filament-enhanced-charts - 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. happenv-com/filament-enhanced-charts

ActiveLibrary

happenv-com/filament-enhanced-charts
====================================

Apache ECharts integration for Filament

v1.0.2(1w ago)0778MITPHPPHP ^8.4CI passing

Since Jul 23Pushed 1w agoCompare

[ Source](https://github.com/happenv-com/filament-enhanced-charts)[ Packagist](https://packagist.org/packages/happenv-com/filament-enhanced-charts)[ Docs](https://github.com/happenv-com/filament-enhanced-charts)[ RSS](/packages/happenv-com-filament-enhanced-charts/feed)WikiDiscussions 1.x Synced 1w ago

READMEChangelog (3)Dependencies (36)Versions (4)Used By (0)

Apache ECharts for Filament
===========================

[](#apache-echarts-for-filament)

[![Latest Version on Packagist](https://camo.githubusercontent.com/b6213447a84bb96b6e3291fa1c8cad4693c4a898ca1a6b981b8ab7abe05f422f/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f68617070656e762d636f6d2f66696c616d656e742d656e68616e6365642d6368617274732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/happenv-com/filament-enhanced-charts)[![GitHub Tests Action Status](https://camo.githubusercontent.com/18ddc1ebd6b19836c5374208c54ae9463424cef9bb79e3bc5102fc2b6b7352bc/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f68617070656e762d636f6d2f66696c616d656e742d656e68616e6365642d6368617274732f74657374732e796d6c3f6272616e63683d312e78266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/happenv-com/filament-enhanced-charts/actions?query=workflow%3A%22Run+Tests%22+branch%3A1.x)[![GitHub Code Style Action Status](https://camo.githubusercontent.com/77d35bf2b7fe4db80d29c91a1cbb69c7c2225b4f5d06d04a334ccd4ba80683b6/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f68617070656e762d636f6d2f66696c616d656e742d656e68616e6365642d6368617274732f70696e742e796d6c3f6272616e63683d312e78266c6162656c3d636f64652532307374796c65267374796c653d666c61742d737175617265)](https://github.com/happenv-com/filament-enhanced-charts/actions?query=workflow%3A%22Fix+PHP+Code+Styling%22+branch%3A1.x)[![Total Downloads](https://camo.githubusercontent.com/4ea349a77ec56a5b9ecfc7756f7b162fbafff129f56846fa8d1e3b4ac296e509/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f68617070656e762d636f6d2f66696c616d656e742d656e68616e6365642d6368617274732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/happenv-com/filament-enhanced-charts)

[Apache ECharts](https://echarts.apache.org/) integration for [Filament](https://filamentphp.com/): dashboard **widgets** and table **columns** driven by a fully typed, fluent PHP option model.

```
class OrdersChart extends EnhancedChartWidget
{
    protected static ?string $heading = 'Orders per day';

    protected function getOption(): Option
    {
        return Option::cartesian()
            ->xAxis(CategoryAxis::make()->data(['Mon', 'Tue', 'Wed']))
            ->series(LineSeries::make()->name('Orders')->smooth()->data([12, 20, 15]));
    }
}
```

- **Typed option model** — the ECharts option tree mirrored as fluent PHP builders. If you know an ECharts option, you know the method. Everything not (yet) modelled stays reachable through a `->raw()` escape hatch on every node.
- **22 series types** — line, bar, pie, scatter, effectScatter, candlestick, boxplot, heatmap, radar, gauge, funnel, sankey, sunburst, treemap, tree, graph, parallel, themeRiver, pictorialBar, map, lines, and fully custom `renderItem` series.
- **Every coordinate system** — cartesian grids, polar, radar, geo/maps, calendar, singleAxis, parallel, matrix — plus datasets with transforms, visual maps, data zoom, toolbox, brush and graphic elements.
- **Filament-native** — panel theming, automatic dark mode, Filament `Color` palettes, `RawJs`for client-side callbacks, Livewire polling, deferred loading, filters.
- **Table columns** — sparklines, candlesticks and donut pies inside table cells via `EnhancedChartColumn`, or any custom per-record chart.

Requirements
------------

[](#requirements)

- PHP `^8.4`
- Filament `^4.0` or `^5.0`

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

[](#installation)

```
composer require happenv-com/filament-enhanced-charts
```

Register the plugin on each Filament panel that should render charts:

```
use Happenv\FilamentEnhancedCharts\FilamentEnhancedChartsPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugins([
            FilamentEnhancedChartsPlugin::make(),
        ]);
}
```

After installing or upgrading, republish the compiled JavaScript assets:

```
php artisan filament:assets
```

Upgrading from 2.x? See [UPGRADE.md](UPGRADE.md).

Your first widget
-----------------

[](#your-first-widget)

A chart widget is a regular Filament widget: extend `EnhancedChartWidget` and return an `Option` from `getOption()`. That is the whole story.

```
use Happenv\FilamentEnhancedCharts\Option\Axis\CategoryAxis;
use Happenv\FilamentEnhancedCharts\Option\Option;
use Happenv\FilamentEnhancedCharts\Option\Series\BarSeries;
use Happenv\FilamentEnhancedCharts\Option\Series\LineSeries;
use Happenv\FilamentEnhancedCharts\Widgets\EnhancedChartWidget;

class BlogPostsChart extends EnhancedChartWidget
{
    protected static ?string $heading = 'Blog posts';

    protected function getOption(): Option
    {
        return Option::cartesian()
            ->xAxis(CategoryAxis::make()->data(['Jan', 'Feb', 'Mar']))
            ->series(
                LineSeries::make()->name('Posts')->smooth()->data([10, 15, 8]),
                BarSeries::make()->name('Comments')->data([45, 60, 32]),
            );
    }
}
```

Two entry points exist on `Option`:

- `Option::make()` — a blank slate.
- `Option::cartesian()` — a batteries-included preset for the common case: an axis-triggered tooltip and a top legend, ready for `->xAxis()` + `->series()`.

When an x-axis is set and no y-axis is given, a `ValueAxis` is defaulted for you. Set `->yAxis(...)` explicitly when you need something else (e.g. a `CategoryAxis` for a heatmap).

The option model
----------------

[](#the-option-model)

Everything under `Happenv\FilamentEnhancedCharts\Option\*` is a fluent builder that serializes to the exact [ECharts option](https://echarts.apache.org/en/option.html) shape. This package deliberately does **not** re-document every chart type — the ECharts docs and [examples gallery](https://echarts.apache.org/examples/en/) are the reference; the builders mirror them method-for-method.

KindClassesRoot`Option`Axes`Axis\CategoryAxis`, `Axis\ValueAxis`, `Axis\TimeAxis`, `Axis\LogAxis`Series`Series\LineSeries`, `BarSeries`, `PieSeries`, `ScatterSeries`, `EffectScatterSeries`, `CandlestickSeries`, `BoxplotSeries`, `HeatmapSeries`, `RadarSeries`, `GaugeSeries`, `FunnelSeries`, `SankeySeries`, `SunburstSeries`, `TreemapSeries`, `TreeSeries`, `GraphSeries`, `ParallelSeries`, `ThemeRiverSeries`, `PictorialBarSeries`, `MapSeries`, `LinesSeries`, `CustomSeries`Components`Component\Legend`, `Tooltip`, `Title`, `Grid`, `DataZoom`, `VisualMap`, `Toolbox` (+ `Toolbox\*` features), `AxisPointer`, `Brush`, `Polar`, `AngleAxis`, `RadiusAxis`, `Radar`, `Geo`, `Calendar`, `SingleAxis`, `Parallel`, `ParallelAxis`, `Matrix`, `Dataset`, `Transform`, `Graphic` (+ `Graphic\*` elements)Styles`Style\ItemStyle`, `LineStyle`, `AreaStyle`, `Label`, `Emphasis`Marks`Mark\MarkLine`, `MarkPoint`, `MarkArea`Data`DataPoint`, `SankeyNode`, `SankeyLink`Conventions that hold across the whole tree:

- **`::make()` + fluent setters.** Every builder is created with `::make()` and configured by chaining; setters return `$this`.
- **One or many.** Slots that ECharts accepts as "an object or an array of objects" are variadic: `->grid(...)`, `->xAxis(...)`, `->title(...)`, `->series(...)`, `->visualMap(...)`, … Pass one or several; the emitted JSON follows ECharts' single-vs-array convention automatically.
- **Builders or arrays.** Sub-config setters accept the matching builder *or* a plain array: `->itemStyle(ItemStyle::make()->borderRadius(4))` and `->itemStyle(['borderRadius' => 4])` are equivalent.
- **Conditional building.** All builders use Laravel's `Conditionable`: `->when($this->filter === 'year', fn (Option $o) => $o->dataZoom(...))`.
- **Per-point configuration.** Wherever a data item can be more than a value, pass a `DataPoint`: `DataPoint::make(1048)->name('Search')->itemStyle(ItemStyle::make()->color('#c23531'))`.

### The `raw()` escape hatch

[](#the-raw-escape-hatch)

Every node (`Option`, each series, axis, component, `DataPoint`, …) accepts `->raw(array $options)`. Raw options are **deep-merged over the typed output, raw wins** — so you can reach any ECharts option the typed API doesn't model yet without abandoning the builders:

```
LineSeries::make()
    ->data($values)
    ->raw(['universalTransition' => true]);
```

To skip the typed model entirely, return a raw tree from a blank `Option`:

```
protected function getOption(): Option
{
    return Option::make()->raw([
        'xAxis' => ['type' => 'category', 'data' => ['A', 'B']],
        'series' => [['type' => 'bar', 'data' => [5, 7]]],
    ]);
}
```

Prefer the typed methods — `raw()` is the last resort, not the default.

Formatters and JS callbacks
---------------------------

[](#formatters-and-js-callbacks)

Strings passed to `formatter()`-style methods are treated as literal [ECharts templates](https://echarts.apache.org/en/option.html#tooltip.formatter) and pass through unchanged. For a real client-side function, pass Filament's `RawJs`: it is serialized as a marker and revived into a genuine JS function in the browser.

```
use Filament\Support\RawJs;
use Happenv\FilamentEnhancedCharts\Option\Axis\ValueAxis;
use Happenv\FilamentEnhancedCharts\Option\Component\Tooltip;

Option::make()
    ->tooltip(Tooltip::make()->trigger('axis')->formatter('{b}: {c} pcs')) // template string
    ->yAxis(ValueAxis::make()->axisLabel(
        RawJs::make('(value) => (value / 1000) + "k"'),                    // executable JS
    ));
```

`RawJs` works anywhere in the tree — `renderItem` on a `CustomSeries`, `symbolSize` callbacks, `animationDelay` functions, `labelLayout`, colors-by-callback, and so on. Inside these callbacks the `echarts` global is available, so gallery snippets using `echarts.format.addCommas(...)` or `echarts.graphic.clipRectByRect(...)` port verbatim.

Numbers
-------

[](#numbers)

Anywhere a number is accepted you may pass a native `\BcMath\Number` instead of `int|float|string`; it is serialized as an exact numeric literal rather than a lossy float.

Feeding data from Eloquent
--------------------------

[](#feeding-data-from-eloquent)

A series' `data()` and an axis' `data()` take any `iterable`, so a `Collection` works — but the **shape** matters:

- **A positional list** (a bare `pluck` or an array) → pass it straight to `data()`. Keys are dropped (the series/axis is positional), so a keyed Collection never mis-serializes to an object:

    ```
    $totals = Order::query()->orderBy('day')->pluck('total');   // [120, 90, 140, …]
    LineSeries::make()->data($totals);
    ```
- **A keyed map** where the key is the label (`pluck('total', 'channel')`, a `groupBy` count) → use `ChartData::fromPairs()` to split it into labels + values:

    ```
    use Happenv\FilamentEnhancedCharts\Data\ChartData;

    $data = ChartData::fromPairs(Order::query()->pluck('total', 'channel')); // ['B2B' => 60, …]

    Option::make()
        ->series(PieSeries::make()->data($data->toDataPoints()));            // named slices
    // or, for a cartesian chart:
    Option::cartesian()
        ->xAxis(CategoryAxis::make()->data($data->labels()))
        ->series(BarSeries::make()->data($data->values()));
    ```
- **Tabular rows for multiple series** → build a `Dataset` and let each series `encode()` its columns. `Dataset::fromModels()` projects a query result to just the chart columns (never the full attribute bag) — don't hand a `Collection` to `data()`:

    ```
    Option::make()
        ->dataset(Dataset::fromModels(
            Order::query()->selectRaw('day, sum(total) total, channel')->get(),
            columns: ['day', 'total', 'channel'],
        ))
        ->series(LineSeries::make()->encode(x: 'day', y: 'total'));
    ```

### Time series

[](#time-series)

For "per day/week/month" aggregations, [`flowframe/laravel-trend`](https://github.com/flowframe/laravel-trend)is the recommended companion: it handles the per-driver date bucketing (Postgres/MySQL/MariaDB/ SQLite) and gap-fills empty periods with zeros. It is an optional `suggest` — install it only if you need time series:

```
composer require flowframe/laravel-trend
```

Feed its result straight to `ChartData::fromTimeSeries()`:

```
use Flowframe\Trend\Trend;
use Happenv\FilamentEnhancedCharts\Data\ChartData;

$data = ChartData::fromTimeSeries(
    Trend::query($this->scopedOrders())
        ->between(now()->subDays(30), now())
        ->perDay()
        ->sum('total'),
);

Option::cartesian()
    ->xAxis(CategoryAxis::make()->data($data->labels()))
    ->series(LineSeries::make()->smooth()->data($data->values()));
```

`ChartData::fromTimeSeries()` reads `date`/`aggregate` off each row with `data_get()`, so it is decoupled from the trend package — any `Collection` of rows carrying a label and a value column works (pass `labelKey:`/`valueKey:` for different column names).

### Shortcut: a chart from just data + a type

[](#shortcut-a-chart-from-just-data--a-type)

When the chart is a single series of one of the common types, the `HasChartData` trait writes `getOption()` for you — declare the data and (optionally) the type, nothing else:

```
use Happenv\FilamentEnhancedCharts\Concerns\HasChartData;
use Happenv\FilamentEnhancedCharts\Data\ChartData;
use Happenv\FilamentEnhancedCharts\Enums\ChartType;
use Happenv\FilamentEnhancedCharts\Widgets\EnhancedChartWidget;

class SalesByChannelChart extends EnhancedChartWidget
{
    use HasChartData;

    protected static ?string $heading = 'Sales by channel';

    protected function getData(): ChartData
    {
        return ChartData::fromPairs(Order::query()->pluck('total', 'channel'));
    }

    protected function chartType(): ChartType
    {
        return ChartType::Bar; // Line (default), Area, Bar, or Pie
    }
}
```

`ChartType` is a curated set — `Line`, `Area`, `Bar`, `Pie` — that maps cleanly onto the `labels + values` (or, for pie, the named `DataPoint`) shape. The trait builds a cartesian option (category x-axis + value y-axis) for the axis types and an axis-less option for pie. The moment you want more — a second series, custom styling, marks, a scatter's x/y pairs — implement `getOption()`directly with the typed builders; this trait is only the shallow-end shortcut.

Colors
------

[](#colors)

Color setters (`color()`, `backgroundColor()`, `borderColor()`, `shadowColor()`, …) accept:

- any CSS color string (`'#7c3aed'`, `'rgb(124, 58, 237)'`),
- an ECharts gradient/pattern array,
- a **Filament palette** — `Color::Amber` resolves to its 500 shade, and Filament's `oklch(...)`values are converted to `rgb()` so ECharts can both paint them and derive hover shades:

```
use Filament\Support\Colors\Color;

BarSeries::make()
    ->color(Color::Emerald)          // 500 shade
    ->data($values);

LineSeries::make()
    ->color(Color::Amber[600])       // an explicit shade
    ->data($values);
```

Dark mode
---------

[](#dark-mode)

Dark mode is automatic. Charts render transparent over the Filament card, and when the panel is in dark mode the package overlays a theme layer client-side — axis lines/labels, split lines, legend, title, tooltip, visual maps, parallel axes and outside series labels are all lightened for readability. The theme reapplies live when the user toggles light/dark. You write the option once; both modes just work.

Tip: skip forced `backgroundColor` and hardcoded text colors in your options — inherit the card and let the theme adapt.

Page scrolling
--------------

[](#page-scrolling)

By default the mouse wheel scrolls the **page**, not the chart: `inside` data zoom stops wheel-zooming (drag-to-pan is kept) and any `roam` is downgraded to drag-pan. That way a dashboard full of charts never traps the user's scroll.

Opt a chart back into wheel-zoom (e.g. a full-bleed map) on the widget:

```
protected static bool $scrollable = false;
```

or per option, which wins over the widget default:

```
Option::make()->scrollable(false);
```

Widget configuration
--------------------

[](#widget-configuration)

```
protected static ?string $heading = 'Revenue';        // or override getHeading()
protected static ?string $subheading = 'Last 30 days'; // or getSubheading()
protected static bool $isCollapsible = true;           // or isCollapsible()
protected static int $contentHeight = 400;             // px, default 300; or getContentHeight()
protected static ?string $footer = 'Updated hourly';   // or getFooter() (string|Htmlable|View)
protected static string $chartId = 'revenueChart';     // stable DOM id, optional
protected static string $renderer = 'canvas';          // or 'svg'; or getRenderer()
protected int | string | array $columnSpan = 'full';
```

The header disappears entirely when no heading, subheading or filters are set.

### Deferred loading

[](#deferred-loading)

Don't hold up the page for a slow query:

```
protected static bool $deferLoading = true;

protected function getOption(): Option
{
    if (! $this->readyToLoad) {
        return Option::make();
    }

    return Option::cartesian()->series(/* the expensive part */);
}
```

### Loading indicator

[](#loading-indicator)

```
protected static ?string $loadingIndicator = 'Loading…'; // or getLoadingIndicator() returning a View
```

### Live updating (polling)

[](#live-updating-polling)

Widgets poll every 5 seconds by default. Change or disable it with an instance property:

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

protected ?string $pollingInterval = null; // disable
```

Updates are diffed server-side — the chart only re-renders when the resolved options actually changed.

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

[](#filtering-chart-data)

### Single select

[](#single-select)

Return options from `getFilters()` and read the active value from `$this->filter`:

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

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

protected function getOption(): Option
{
    $data = match ($this->filter) {
        'week' => $this->weekly(),
        'month' => $this->monthly(),
        'year' => $this->yearly(),
    };

    return Option::cartesian()->series(LineSeries::make()->data($data));
}
```

A select appears in the widget header and the chart updates live on change.

### Filter schema

[](#filter-schema)

For a richer filter form, implement the package's `HasFiltersSchema` **contract** and use Filament's chart-widget filters trait; filter values arrive in `$this->filters`:

```
use Filament\Forms\Components\DatePicker;
use Filament\Schemas\Schema;
use Filament\Widgets\ChartWidget\Concerns\HasFiltersSchema;
use Happenv\FilamentEnhancedCharts\Contracts\HasFiltersSchema as HasFiltersSchemaContract;
use Happenv\FilamentEnhancedCharts\Widgets\EnhancedChartWidget;

class BlogPostsChart extends EnhancedChartWidget implements HasFiltersSchemaContract
{
    use HasFiltersSchema;

    public function filtersSchema(Schema $schema): Schema
    {
        return $schema->components([
            DatePicker::make('date_start')->default(now()->subMonth()),
            DatePicker::make('date_end')->default(now()),
        ]);
    }

    public function updatedInteractsWithSchemas(string $statePath): void
    {
        parent::updatedInteractsWithSchemas($statePath);

        $this->updateOptions();
    }

    protected function getOption(): Option
    {
        $start = $this->filters['date_start'] ?? null;
        $end = $this->filters['date_end'] ?? null;

        // ...
    }
}
```

Implementing the contract is what makes the filter dropdown render — don't skip it.

Maps (GeoJSON)
--------------

[](#maps-geojson)

A `MapSeries` or `geo` component needs its map registered client-side. Return `name => url` pairs from `getMaps()`; each is fetched and passed to `echarts.registerMap()` before first paint:

```
use Happenv\FilamentEnhancedCharts\Option\Component\VisualMap;
use Happenv\FilamentEnhancedCharts\Option\Series\MapSeries;

/** @return array */
public function getMaps(): array
{
    return ['world' => asset('geo/world.json')];
}

protected function getOption(): Option
{
    return Option::make()
        ->visualMap(VisualMap::continuous()->min(0)->max(100)->calculable())
        ->series(MapSeries::make()->map('world')->roam()->data($rows));
}
```

Charts in table cells
---------------------

[](#charts-in-table-cells)

`EnhancedChartColumn` renders a small chart per row. Three presets cover the common cases; `chart()`takes over for anything else. Return `null`/empty data to render nothing for that row.

```
use Happenv\FilamentEnhancedCharts\Columns\EnhancedChartColumn;

EnhancedChartColumn::make('trend')
    ->sparkline(fn (Product $record): array => $record->daily_sales)
    ->fill(),                                      // area fill; ->bars() for bar sparklines

EnhancedChartColumn::make('ohlc')
    ->width(160)                                   // px, '100%', or a Closure; height(int) too
    ->candles(fn (Product $record): array => $record->ohlc_rows), // [open, close, low, high] rows

EnhancedChartColumn::make('mix')
    ->pie(fn (Order $record): array => ['B2B' => 60, 'B2C' => 40]), // donut

EnhancedChartColumn::make('custom')
    ->chart(fn (Server $record): ?Option => Option::make()
        ->series(GaugeSeries::make()->data([DataPoint::make($record->cpu)]))),
```

Cell charts ship with a sensible baseline (tooltip escaping the cell, no legend, tight margins) and are disposed cleanly as rows leave the DOM. `->renderer('svg')` is available per column. Custom `->chart()` closures can start from the same baseline via `EnhancedChartColumn::cellOption()` (optionally passing the tooltip trigger, e.g. `'axis'`).

Publishing views / translations / config
----------------------------------------

[](#publishing-views--translations--config)

```
php artisan vendor:publish --tag="filament-enhanced-charts-views"
php artisan vendor:publish --tag="filament-enhanced-charts-translations"
php artisan vendor:publish --tag="filament-enhanced-charts-config"
```

Testing
-------

[](#testing)

The package registers a Livewire `Testable` mixin with chart assertions for your app's tests:

```
use function Pest\Livewire\livewire;

livewire(RevenueChart::class)
    ->assertChartSeriesCount(2)
    ->assertChartHasSeries('line')
    ->assertChartOptions(fn (array $options): bool => $options['series'][0]['smooth'] === true);
```

To run the package's own test suite:

```
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)

- [elemind](https://github.com/elemind)
- Strongly inspired by [Leandro Ferreira's Apex Charts plugin](https://filamentphp.com/plugins/leandrocfe-apex-charts)
- [All Contributors](../../contributors)

License
-------

[](#license)

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

###  Health Score

48

—

FairBetter than 94% of packages

Maintenance98

Actively maintained with recent releases

Popularity20

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 81.8% 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 ~8 days

Total

4

Last Release

13d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/855788?v=4)[Bartłomiej Gajda](/maintainers/webard)[@webard](https://github.com/webard)

---

Top Contributors

[![webard](https://avatars.githubusercontent.com/u/855788?v=4)](https://github.com/webard "webard (9 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (2 commits)")

---

Tags

laravelfilamentphpfilament-echartshappenv

###  Code Quality

TestsPest

Static AnalysisPHPStan, Rector

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/happenv-com-filament-enhanced-charts/health.svg)

```
[![Health](https://phpackages.com/badges/happenv-com-filament-enhanced-charts/health.svg)](https://phpackages.com/packages/happenv-com-filament-enhanced-charts)
```

###  Alternatives

[backstage/mails

View logged mails and events in a beautiful Filament UI.

16429.7k](/packages/backstage-mails)[relaticle/custom-fields

User Defined Custom Fields for Laravel Filament

16472.9k](/packages/relaticle-custom-fields)[awcodes/filament-curator

A media picker plugin for FilamentPHP.

441414.6k29](/packages/awcodes-filament-curator)[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3915.5k](/packages/rawilk-profile-filament-plugin)[marcelweidum/filament-passkeys

Use passkeys in your filamentphp app

6769.9k2](/packages/marcelweidum-filament-passkeys)[stephenjude/filament-two-factor-authentication

Filament Two Factor Authentication: Google 2FA + Passkey Authentication

85264.8k10](/packages/stephenjude-filament-two-factor-authentication)

PHPackages © 2026

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