PHPackages                             laraveldaily/filawidgets - 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. laraveldaily/filawidgets

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

laraveldaily/filawidgets
========================

Reusable Filament dashboard widgets.

v0.1.2(3mo ago)461.2k↑1550%4MITPHPPHP ^8.2

Since Mar 24Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/LaravelDaily/FilaWidgets)[ Packagist](https://packagist.org/packages/laraveldaily/filawidgets)[ RSS](/packages/laraveldaily-filawidgets/feed)WikiDiscussions main Synced 3w ago

READMEChangelog (1)Dependencies (8)Versions (3)Used By (0)

FilaWidgets
===========

[](#filawidgets)

Reusable Filament dashboard widgets for Laravel. Five widget types — sparkline tables, breakdowns, progress bars, completion rate gauges, and heatmap calendars — that work on dashboards, resource pages, or any Filament page.

[![](https://camo.githubusercontent.com/3cebb90b159f34145a2f12e3d06ff9bf206946c759f43b663503b2ca85688134/68747470733a2f2f6c61726176656c6461696c792e636f6d2f75706c6f6164732f323032362f30332f66696c61776964676574732d6d61696e6578616d706c652d30312d6c6162656c732e706e67)](https://camo.githubusercontent.com/3cebb90b159f34145a2f12e3d06ff9bf206946c759f43b663503b2ca85688134/68747470733a2f2f6c61726176656c6461696c792e636f6d2f75706c6f6164732f323032362f30332f66696c61776964676574732d6d61696e6578616d706c652d30312d6c6162656c732e706e67)

---

[![](https://camo.githubusercontent.com/0a8e48ae82c8e4daf3a83725f899755f2176580ce2a0342b1dac030372727952/68747470733a2f2f6c61726176656c6461696c792e636f6d2f75706c6f6164732f323032362f30332f66696c61776964676574732d6d61696e6578616d706c652d30322d6c6162656c732e706e67)](https://camo.githubusercontent.com/0a8e48ae82c8e4daf3a83725f899755f2176580ce2a0342b1dac030372727952/68747470733a2f2f6c61726176656c6461696c792e636f6d2f75706c6f6164732f323032362f30332f66696c61776964676574732d6d61696e6578616d706c652d30322d6c6162656c732e706e67)

---

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

[](#requirements)

- PHP 8.2+
- Laravel 11+
- Filament 4+

---

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

[](#installation)

```
composer require laraveldaily/filawidgets
```

The service provider auto-registers via Composer's package discovery.

### Custom Theme (Required)

[](#custom-theme-required)

This package uses Tailwind CSS classes that are not included in Filament's default compiled styles. You need a [custom Filament theme](https://filamentphp.com/docs/5.x/styling/overview) so that Tailwind can scan the package's Blade views.

**1. Create a theme** (if you don't have one already):

```
php artisan make:filament-theme
```

Follow the instructions the command prints — it will add the theme to `vite.config.js` and your panel provider.

**2. Add the package views to your theme CSS file** (`resources/css/filament/{panel}/theme.css`):

```
@import '../../../../vendor/filament/filament/resources/css/theme.css';

@source '../../../../app/Filament/**/*';
@source '../../../../resources/views/filament/**/*';
@source '../../../../vendor/laraveldaily/filawidgets/resources/views/**/*';
```

Add this last line with `vendor/laraveldaily/filawidgets`.

**3. Rebuild your assets:**

```
npm run build
```

(*or `npm run dev`*)

---

Quick Start: Create Widgets
---------------------------

[](#quick-start-create-widgets)

Scaffold a widget with the artisan command:

```
php artisan make:filawidget
```

The command prompts for a name and widget type interactively. You can also pass them directly:

```
php artisan make:filawidget RevenueByRegionWidget --type=Breakdown
```

Available types: `SparklineTable`, `Breakdown`, `Progress`, `CompletionRate`, `HeatmapCalendar`.

The generated class is placed in `app/Filament/Widgets/` with a minimal skeleton — just fill in your query logic.

---

Every widget follows the same pattern: extend the base class, set properties, implement `getData()`.

```
use LaravelDaily\FilaWidgets\Data\BreakdownItemData;
use LaravelDaily\FilaWidgets\Data\BreakdownWidgetData;
use LaravelDaily\FilaWidgets\Widgets\BreakdownWidget;

class RevenueByRegionWidget extends BreakdownWidget
{
    protected ?string $widgetLabel = 'Revenue by Region';
    protected ?int $itemLimit = 4;
    protected bool $groupOther = true;

    protected function getData(): BreakdownWidgetData
    {
        // Your query logic here
        return new BreakdownWidgetData(items: [
            new BreakdownItemData('United States', 27378.77, previousValue: 23120.50),
            new BreakdownItemData('Germany', 17230.02, previousValue: 13520.00),
            new BreakdownItemData('Lithuania', 11989.15, previousValue: 8840.30),
        ], description: 'Country mix for last 30 days');
    }
}
```

### Displaying Widgets

[](#displaying-widgets)

**Dashboard (auto-discovery):** Filament auto-discovers widgets in `app/Filament/Widgets/`, so widgets created with `make:filawidget` appear on the default dashboard automatically — no registration needed.

**Custom dashboard page:** If you have a [custom dashboard](https://filamentphp.com/docs/5.x/panels/dashboard), register widgets explicitly:

```
public function getWidgets(): array
{
    return [RevenueByRegionWidget::class];
}
```

**Resource or custom pages:** Add widgets to any page's header or footer:

```
protected function getHeaderWidgets(): array
{
    return [
        RevenueByRegionWidget::make(['range' => 'last_30_days']),
    ];
}
```

When `range` is passed directly, the widget does not depend on dashboard page filters.

---

Widget Types
------------

[](#widget-types)

### 1. SparklineTableWidget

[](#1-sparklinetablewidget)

A multi-row metric table with inline SVG sparkline charts and trend badges.

**Base class:** `LaravelDaily\FilaWidgets\Widgets\SparklineTableWidget`**Default column span:** `['md' => 1, 'xl' => 3]`

```
use LaravelDaily\FilaWidgets\Data\SparklineTableRowData;
use LaravelDaily\FilaWidgets\Data\SparklineTableWidgetData;
use LaravelDaily\FilaWidgets\Support\SparklineSeries;
use LaravelDaily\FilaWidgets\Widgets\SparklineTableWidget;

class RevenuePulseWidget extends SparklineTableWidget
{
    protected ?string $widgetLabel = 'Revenue Pulse';

    protected function getData(): SparklineTableWidgetData
    {
        $dateRange = DashboardDateRange::fromFilter($this->getRangeFilter());
        [$currentStart, $currentEnd] = $dateRange->currentPeriod();
        [$previousStart, $previousEnd] = $dateRange->previousPeriod();

        $baseQuery = Order::query()->where('status', OrderStatus::Completed);

        return SparklineTableWidgetData::fromRows(
            new SparklineTableRowData(
                label: 'Revenue',
                value: (float) (clone $baseQuery)->whereBetween('created_at', [$currentStart, $currentEnd])->sum('amount'),
                previousValue: (float) (clone $baseQuery)->whereBetween('created_at', [$previousStart, $previousEnd])->sum('amount'),
                sparkline: SparklineSeries::daily($currentStart, $currentEnd, clone $baseQuery, 'SUM(amount)'),
                format: 'currency',
            ),
            new SparklineTableRowData(
                label: 'Orders',
                value: (float) (clone $baseQuery)->whereBetween('created_at', [$currentStart, $currentEnd])->count(),
                previousValue: (float) (clone $baseQuery)->whereBetween('created_at', [$previousStart, $previousEnd])->count(),
                sparkline: SparklineSeries::daily($currentStart, $currentEnd, clone $baseQuery, 'COUNT(*)'),
                format: 'number',
                precision: 0,
            ),
        );
    }
}
```

#### SparklineTableRowData

[](#sparklinetablerowdata)

ParameterTypeDefaultDescription`label``string`requiredRow label`value``float`requiredCurrent period value`previousValue``?float``null`Previous period value (enables trend badge)`sparkline``array``[]`Ordered daily data points for the SVG chart`format``?string``null``'currency'`, `'number'`, or `'percentage'` (falls back to widget default)`precision``?int``null`Decimal places (falls back to widget default)`url``?string``null`Makes the row clickable`openUrlInNewTab``bool``false`Open URL in new tab`color``?string``null`Per-row color (`'success'`, `'warning'`, `'danger'`) for sparkline and badge`showSparkline``bool``true`Set `false` to hide the sparkline for this row#### SparklineSeries Helper

[](#sparklineseries-helper)

Builds a zero-filled daily float array from a query. Eliminates the most common boilerplate:

```
use LaravelDaily\FilaWidgets\Support\SparklineSeries;

$series = SparklineSeries::daily(
    start: $start,                    // CarbonInterface
    end: $end,                        // CarbonInterface
    query: Order::query()->where(...), // Eloquent Builder (cloned internally)
    aggregate: 'SUM(amount)',          // Raw SQL aggregate
    dateColumn: 'created_at',          // Column to group by (default)
    precision: 2,                      // Decimal rounding (default)
);
// Returns: [0.0, 150.50, 0.0, 320.00, ...]  (one float per day)
```

---

### 2. BreakdownWidget

[](#2-breakdownwidget)

A ranked list showing each item's value, contribution percentage, and period-over-period delta.

**Base class:** `LaravelDaily\FilaWidgets\Widgets\BreakdownWidget`**Default column span:** `['md' => 1, 'xl' => 3]`

```
use LaravelDaily\FilaWidgets\Data\BreakdownItemData;
use LaravelDaily\FilaWidgets\Data\BreakdownWidgetData;
use LaravelDaily\FilaWidgets\Widgets\BreakdownWidget;

class RevenueByRegionWidget extends BreakdownWidget
{
    protected ?string $widgetLabel = 'Revenue by Region';
    protected ?int $itemLimit = 4;
    protected bool $groupOther = true;

    protected function getData(): BreakdownWidgetData
    {
        $dateRange = DashboardDateRange::fromFilter($this->getRangeFilter());
        [$currentStart, $currentEnd] = $dateRange->currentPeriod();
        [$previousStart, $previousEnd] = $dateRange->previousPeriod();

        $currentItems = $this->totalsByCountry($currentStart, $currentEnd);
        $previousItems = $this->totalsByCountry($previousStart, $previousEnd);

        $items = $currentItems
            ->keys()
            ->merge($previousItems->keys())
            ->unique()
            ->map(fn (string $country): BreakdownItemData => new BreakdownItemData(
                label: $this->countryName($country),
                value: (float) ($currentItems[$country] ?? 0),
                previousValue: (float) ($previousItems[$country] ?? 0),
            ))
            ->sortByDesc(fn (BreakdownItemData $item): float => $item->value)
            ->values()
            ->all();

        return new BreakdownWidgetData(
            items: $items,
            description: 'Country mix for ' . strtolower($dateRange->label()),
        );
    }
}
```

#### BreakdownWidget Properties

[](#breakdownwidget-properties)

PropertyTypeDefaultDescription`$itemLimit``?int``null`Max rows to display`$groupOther``bool``false`Aggregate overflow rows into "Other"`$sortBy``string``'value'`Sort field (`'value'` or `'label'`)`$sortDirection``string``'desc'``'asc'` or `'desc'``$showContribution``bool``true`Show contribution percentage column`$showDelta``bool``true`Show delta percentage badge`$deltaThresholds``array``[]`Color thresholds for delta badges#### BreakdownItemData

[](#breakdownitemdata)

ParameterTypeDefaultDescription`label``string`requiredItem name`value``float`requiredCurrent value`previousValue``?float``null`Previous period value (enables delta)`color``?string``null`Row color (`'success'`, `'warning'`, `'danger'`, `'primary'`)`icon``?string``null`Heroicon name for the row`url``?string``null`Makes the row clickable#### BreakdownWidgetData::fromCollection()

[](#breakdownwidgetdatafromcollection)

Build from query results without manual mapping:

```
// From an array of associative arrays
$data = BreakdownWidgetData::fromCollection(
    items: $queryResults,
    labelKey: 'category_name',
    valueKey: 'total_revenue',
    previousValueKey: 'previous_revenue',
    description: 'Revenue breakdown',
);

// From a keyed collection using closures
$data = BreakdownWidgetData::fromCollection(
    items: $totals,  // Collection  e.g. ['US' => 15000, 'DE' => 8000]
    labelKey: fn ($value, $key) => $key,
    valueKey: fn ($value) => (float) $value,
);
```

---

### 3. ProgressWidget

[](#3-progresswidget)

A horizontal progress bar with goal tracking and optional projection.

**Base class:** `LaravelDaily\FilaWidgets\Widgets\ProgressWidget`**Default column span:** `['md' => 1, 'xl' => 2]`

```
use LaravelDaily\FilaWidgets\Widgets\ProgressWidget;

class RevenueGoalWidget extends ProgressWidget
{
    protected ?string $widgetLabel = 'Revenue Goal';
    protected float $goal = 50000;
    protected int $goalRangeDays = 30;

    protected function getCurrentValue(): float
    {
        $dateRange = DashboardDateRange::fromFilter($this->getRangeFilter());
        [$start, $end] = $dateRange->currentPeriod();

        return (float) Order::query()
            ->where('status', OrderStatus::Completed->value)
            ->whereBetween('created_at', [$start, $end])
            ->sum('amount');
    }
}
```

#### ProgressWidget Properties

[](#progresswidget-properties)

PropertyTypeDefaultDescription`$goal``float``0`Target value`$goalRangeDays``int``30`Days for projection calculation`$showProjection``bool``true`Show projected pace box---

### 4. CompletionRateWidget

[](#4-completionratewidget)

An SVG arc gauge showing a completion rate with threshold-based coloring.

**Base class:** `LaravelDaily\FilaWidgets\Widgets\CompletionRateWidget`**Default column span:** `['md' => 1, 'xl' => 2]`

```
use LaravelDaily\FilaWidgets\Widgets\CompletionRateWidget;

class FulfillmentRateWidget extends CompletionRateWidget
{
    protected ?string $widgetLabel = 'Fulfillment Rate';

    protected function getCounts(): array
    {
        $dateRange = DashboardDateRange::fromFilter($this->getRangeFilter());
        [$start, $end] = $dateRange->currentPeriod();

        $completed = Order::query()
            ->where('status', OrderStatus::Completed->value)
            ->whereBetween('created_at', [$start, $end])
            ->count();

        $total = Order::query()
            ->whereBetween('created_at', [$start, $end])
            ->count();

        return ['completed' => $completed, 'total' => $total];
    }

    protected function getThresholds(): array
    {
        return [
            ['threshold' => 50, 'color' => 'danger', 'label' => 'Critical'],
            ['threshold' => 75, 'color' => 'warning', 'label' => 'Needs attention'],
            ['threshold' => 100, 'color' => 'success', 'label' => 'Healthy'],
        ];
    }
}
```

---

### 5. HeatmapCalendarWidget

[](#5-heatmapcalendarwidget)

A GitHub-style heatmap grid showing daily activity density.

**Base class:** `LaravelDaily\FilaWidgets\Widgets\HeatmapCalendarWidget`**Default column span:** `['md' => 2, 'xl' => 2]`

```
use LaravelDaily\FilaWidgets\Data\HeatmapCalendarWidgetData;
use LaravelDaily\FilaWidgets\Widgets\HeatmapCalendarWidget;

class DailyRevenueWidget extends HeatmapCalendarWidget
{
    protected ?string $widgetLabel = 'Daily Revenue';

    protected function getData(): HeatmapCalendarWidgetData
    {
        $dateRange = DashboardDateRange::fromFilter($this->getRangeFilter());
        [$start, $end] = $dateRange->currentPeriod();

        $entries = Order::query()
            ->where('status', OrderStatus::Completed)
            ->whereBetween('created_at', [$start, $end])
            ->selectRaw('DATE(created_at) as date, SUM(amount) as total')
            ->groupBy('date')
            ->orderBy('date')
            ->pluck('total', 'date')
            ->map(fn ($value): float => round((float) $value, 2))
            ->all();

        return new HeatmapCalendarWidgetData(
            entries: $entries,  // ['2026-03-20' => 500.00, ...]
            description: 'Daily revenue for ' . strtolower($dateRange->label()),
        );
    }

    protected function getWeeksToShow(): int
    {
        return 9;
    }
}
```

#### HeatmapCalendarWidget Methods

[](#heatmapcalendarwidget-methods)

MethodReturn TypeDefaultDescription`getWeeksToShow()``int``12`Number of weeks to display`getColorScheme()``string``'green'`Color scheme: `'green'` or `'blue'`#### HeatmapCalendarWidgetData

[](#heatmapcalendarwidgetdata)

ParameterTypeDefaultDescription`entries``array`requiredDate-keyed values (`'Y-m-d' => float`)`description``?string``null`Subtitle text`entryUrls``array``[]`Date-keyed URLs for clickable cells`openEntryUrlsInNewTab``bool``false`Open cell URLs in new tab---

Shared Configuration
--------------------

[](#shared-configuration)

All widgets inherit these properties from `InteractsWithWidgetConfiguration`:

PropertyTypeDefaultDescription`$widgetLabel``?string``null`Widget title`$widgetFormat``string``'currency'`Value format: `'currency'`, `'number'`, `'percentage'``$widgetCurrency``string``'USD'`ISO 4217 currency code`$widgetPrecision``int``2`Decimal places`$widgetColor``string``'primary'`Theme color: `'primary'`, `'success'`, `'warning'`, `'danger'``$widgetIcon``Heroicon|string|null``null`Icon (Heroicon enum or string)`$widgetEmptyStateHeading``?string``null`Empty state title`$widgetEmptyStateDescription``?string``null`Empty state subtitle`$widgetActionLabel``?string``null`CTA button label`$widgetActionUrl``?string``null`CTA button URL`$widgetActionOpenInNewTab``bool``false`Open CTA in new tab`$widgetCacheTtl``?int``null`Cache TTL in seconds (`null` = no caching)`$widgetCacheKey``?string``null`Custom cache key prefix`$range``?string``null`Date range filter (public Livewire property)### Date Range Filtering

[](#date-range-filtering)

Widgets resolve the date range from two sources, in priority order:

1. **`$range` property** (public Livewire prop) — passed directly via `Widget::make(['range' => 'last_7_days'])`
2. **`$pageFilters['range']`** — populated automatically on dashboard pages with `HasFiltersForm`

Access it in your widget with `$this->getRangeFilter()`, which returns the resolved `?string` value.

Built-in range values: `'last_7_days'`, `'last_30_days'` (default), `'last_60_days'`.

---

Caching
-------

[](#caching)

Enable caching on any widget by setting the TTL:

```
protected ?int $widgetCacheTtl = 300;  // 5 minutes
protected ?string $widgetCacheKey = 'my-widget';  // Optional prefix
```

Cache keys are generated from the widget class, resolver, current filters, and options. Different filter combinations produce different cache entries automatically.

---

Value Formatting
----------------

[](#value-formatting)

The `WidgetValueFormatter` supports three formats:

FormatExampleDescription`'currency'``$1,234.56`Uses `Number::currency()` with configured currency code`'number'``1,234`Plain number with `number_format()``'percentage'``85.67%`Number with `%` suffixSet the format per widget (`$widgetFormat`) or per sparkline row (`format` parameter).

---

License
-------

[](#license)

MIT

###  Health Score

43

—

FairBetter than 90% of packages

Maintenance82

Actively maintained with recent releases

Popularity33

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity37

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% 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 ~0 days

Total

2

Last Release

95d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/8dfd7abe6d76f60f1fe0b5d24abd419f4945bd41766823f9134bb877bb3f5a34?d=identicon)[Laraveldaily](/maintainers/Laraveldaily)

---

Top Contributors

[![PovilasKorop](https://avatars.githubusercontent.com/u/1510147?v=4)](https://github.com/PovilasKorop "PovilasKorop (9 commits)")

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/laraveldaily-filawidgets/health.svg)

```
[![Health](https://phpackages.com/badges/laraveldaily-filawidgets/health.svg)](https://phpackages.com/packages/laraveldaily-filawidgets)
```

###  Alternatives

[crumbls/layup

A visual page builder plugin for Filament 5 — Divi-style grid layouts with extensible widgets.

591.7k1](/packages/crumbls-layup)[markwalet/nova-modal-response

A Laravel Nova asset for Modal responses on an action.

17818.7k](/packages/markwalet-nova-modal-response)[codewithdennis/larament

Larament is a time-saving starter kit to quickly launch Laravel 13.x projects. It includes FilamentPHP 5.x pre-installed and configured, along with additional tools and features to streamline your development workflow.

3861.7k](/packages/codewithdennis-larament)[tanthammar/filament-extras

Filament macros, pages, fields, columns and other helpers

535.0k](/packages/tanthammar-filament-extras)

PHPackages © 2026

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