PHPackages                             rjp2525/laravel-dashboards - 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. [Admin Panels](/categories/admin)
4. /
5. rjp2525/laravel-dashboards

ActiveLibrary[Admin Panels](/categories/admin)

rjp2525/laravel-dashboards
==========================

A dynamic, customizable dashboard package for Laravel with Vue 3, Inertia.js, and GridStack.js

v0.1.4(4mo ago)14↓93.3%[2 PRs](https://github.com/rjp2525/laravel-dashboards/pulls)MITPHPPHP ^8.4CI passing

Since Feb 23Pushed 2mo agoCompare

[ Source](https://github.com/rjp2525/laravel-dashboards)[ Packagist](https://packagist.org/packages/rjp2525/laravel-dashboards)[ Docs](https://github.com/rjp2525/laravel-dashboards)[ GitHub Sponsors](https://github.com/rjp2525)[ RSS](/packages/rjp2525-laravel-dashboards/feed)WikiDiscussions master Synced today

READMEChangelog (5)Dependencies (15)Versions (10)Used By (0)

Dynamic dashboards for Laravel
==============================

[](#dynamic-dashboards-for-laravel)

 [![Dynamic dashboards for Laravel](.github/laravel-dashboards.png)](.github/laravel-dashboards.png)

[![Latest Version on Packagist](https://camo.githubusercontent.com/dc4e4e1134986dce6a695f0704b51e270e2913e52593eabb37d71faac0623a8e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f726a70323532352f6c61726176656c2d64617368626f617264732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/rjp2525/laravel-dashboards)[![Tests](https://camo.githubusercontent.com/ab9ef66756dab4485f4fad5dfc0a552eef3bc2bb42db1e074c646273ce6f9bc9/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f726a70323532352f6c61726176656c2d64617368626f617264732f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/rjp2525/laravel-dashboards/actions/workflows/run-tests.yml)[![Fix PHP Code Style](https://camo.githubusercontent.com/cebfd7c2a90e8a23dda779f10a34b7cca64c8e18a8483f01f097b37b0d02b119/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f726a70323532352f6c61726176656c2d64617368626f617264732f6669782d7068702d636f64652d7374796c652d6973737565732e796d6c3f6272616e63683d6d61696e266c6162656c3d636f64652532307374796c65267374796c653d666c61742d737175617265)](https://github.com/rjp2525/laravel-dashboards/actions/workflows/fix-php-code-style-issues.yml)[![Total Downloads](https://camo.githubusercontent.com/eeb5161bc82beac9c2b1d2b38598910f2b3881d79aa3997cf29dc10e84d015ad/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f726a70323532352f6c61726176656c2d64617368626f617264732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/rjp2525/laravel-dashboards)

This package provides a full-featured, customizable dashboard system for Laravel applications. It pairs a powerful PHP backend of widget registration, data providers, caching, ACL, presets, real-time broadcasting with a Vue 3 + Inertia.js + GridStack.js frontend that lets users drag, drop and resize widgets.

Table of Contents- [Installation](#installation)
- [Usage](#usage)
    - [Registering widgets](#registering-widgets)
    - [Attribute-based widget discovery](#attribute-based-widget-discovery)
    - [Widget types](#widget-types)
    - [Data providers](#data-providers)
    - [Periods and comparison](#periods-and-comparison)
    - [Layouts and presets](#layouts-and-presets)
    - [Authorization](#authorization)
    - [Caching](#caching)
    - [Real-time updates](#real-time-updates)
    - [Livewire integration](#livewire-integration)
    - [Exporting](#exporting)
    - [Multi-tenancy](#multi-tenancy)
    - [API endpoints](#api-endpoints)
    - [Frontend](#frontend)
    - [Error handling](#error-handling)
    - [Configuration](#configuration)
- [Testing](#testing)
- [Changelog](#changelog)
- [Contributing](#contributing)
- [Security Vulnerabilities](#security-vulnerabilities)
- [Credits](#credits)
- [License](#license)

Once installed you can create dashboards like this:

```
use Reno\Dashboard\Facades\Dashboard;
use Reno\Dashboard\Enums\WidgetType;
use App\Models\Order;

Dashboard::widget('revenue')
    ->label('Revenue')
    ->type(WidgetType::STAT)
    ->using(fn ($context) => WidgetData::stat(
        value: Order::whereBetween('created_at', $context->dateRange())->sum('total'),
    ))
    ->pollEvery(30)
    ->cache(300)
    ->register();
```

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

[](#installation)

You can install the package via Composer:

```
composer require rjp2525/laravel-dashboards
```

Run the install command to publish the config file and run migrations:

```
php artisan dashboard:install
```

You can publish the config file manually with:

```
php artisan vendor:publish --tag="dashboard-config"
```

And the migrations with:

```
php artisan vendor:publish --tag="dashboard-migrations"
```

Usage
-----

[](#usage)

### Registering widgets

[](#registering-widgets)

The most common way to register widgets is through the fluent builder on the `Dashboard` facade, typically in a service provider:

```
use Reno\Dashboard\Facades\Dashboard;
use Reno\Dashboard\Enums\WidgetType;
use Reno\Dashboard\Support\WidgetData;

// Stat widget with a simple callback
Dashboard::widget('total-users')
    ->label('Total Users')
    ->type(WidgetType::STAT)
    ->icon('users')
    ->using(fn ($context) => WidgetData::stat(
        value: User::count(),
    ))
    ->cache(600)
    ->register();

// Chart widget using an Eloquent data provider
Dashboard::widget('signups-chart')
    ->label('Daily Signups')
    ->type(WidgetType::LINE)
    ->provider(
        EloquentDataProvider::for(User::class)
            ->count()
            ->dateColumn('created_at')
    )
    ->position(0, 0, 6, 3)
    ->pollEvery(60)
    ->register();
```

You can also create dedicated widget classes:

```
php artisan dashboard:widget RevenueWidget
```

This generates a widget class in `app/Dashboard/Widgets`:

```
use Reno\Dashboard\Widgets\StatWidget;
use Reno\Dashboard\Contracts\DataProvider;
use Reno\Dashboard\DataProviders\EloquentDataProvider;

class RevenueWidget extends StatWidget
{
    public function key(): string
    {
        return 'revenue';
    }

    public function label(): string
    {
        return 'Revenue';
    }

    public function dataProvider(): DataProvider
    {
        return EloquentDataProvider::for(Order::class)
            ->sum('total')
            ->dateColumn('created_at');
    }
}
```

Register class-based widgets using the manager:

```
use Reno\Dashboard\Facades\Dashboard;

Dashboard::register(RevenueWidget::class);
```

### Attribute-based widget discovery

[](#attribute-based-widget-discovery)

For the simplest setup, annotate your Eloquent models or service classes with PHP attributes and the package will auto-discover and register widgets for you.

**Stat widgets on models** — add `#[DashboardStat]` to generate stat widgets backed by `EloquentDataProvider`:

```
use Reno\Dashboard\Attributes\Dashboardable;
use Reno\Dashboard\Attributes\DashboardStat;

#[Dashboardable(dateColumn: 'ordered_at', dashboard: 'sales')]
#[DashboardStat(label: 'Total Orders', aggregate: 'count')]
#[DashboardStat(label: 'Revenue', aggregate: 'sum', column: 'total_amount', icon: 'currency-dollar')]
class Order extends Model
{
    // ...
}
```

The `#[Dashboardable]` attribute is optional and provides shared defaults (date column, dashboard scope, model scope) that cascade to all `#[DashboardStat]` on the same class. Each stat attribute can override these defaults individually.

Widget keys are auto-generated from the model name and aggregate: `order_count`, `order_sum_total_amount`. You can set a custom key via the `key` parameter.

Supported aggregates: `count`, `sum`, `avg`, `min`, `max`.

**Custom widgets on methods** — use `#[AsWidget]` on a `public static` method that accepts `WidgetContext` and returns `WidgetData`:

```
use Reno\Dashboard\Attributes\AsWidget;
use Reno\Dashboard\Enums\WidgetType;
use Reno\Dashboard\Support\WidgetContext;
use Reno\Dashboard\Support\WidgetData;

class AnalyticsService
{
    #[AsWidget(key: 'conversion_rate', label: 'Conversion Rate', type: WidgetType::STAT)]
    public static function conversionRate(WidgetContext $context): WidgetData
    {
        $rate = // ... your logic
        return WidgetData::stat(value: $rate);
    }
}
```

**Configuration** — discovery is enabled by default. Configure which directories to scan in `config/dashboard.php`:

```
'discovery' => [
    'enabled' => true,
    'paths' => [
        app_path('Models'),
        app_path('Widgets'),
    ],
],
```

**Production caching** — for production, cache the discovery manifest to avoid scanning on every request:

```
php artisan dashboard:discover-cache
```

Clear the cache during deployment or development:

```
php artisan dashboard:discover-clear
```

All attributes support `cacheTtl`, `permissions`, `dashboard`, and `icon` parameters for fine-grained control.

### Widget types

[](#widget-types)

The package ships with the following widget types:

TypeClassDescription`stat``StatWidget`Single number with change indicator`line``ChartWidget`Line chart`bar``ChartWidget`Bar chart`area``ChartWidget`Area chart`pie``PieChartWidget`Pie chart`donut``PieChartWidget`Donut chart`table``TableWidget`Data table with pagination`listing``ListWidget`Simple list of items`progress``ProgressWidget`Progress bar`heatmap``HeatmapWidget`GitHub-style contribution heatmap`status_timeline``StatusTimelineWidget`Service uptime timeline`sparkline``SparklineWidget`Stat with inline sparkline chart`progress_circle``ProgressCircleWidget`Circular/radial progress indicator`bar_list``BarListWidget`Ranked horizontal bar list`funnel``FunnelWidget`Conversion funnel visualization`category``CategoryWidget`Category breakdown display`budget``BudgetWidget`Budget vs. actual comparison`gauge``GaugeWidget`Gauge/dial meter`custom``CustomWidget`Your own Vue component### Data providers

[](#data-providers)

Data providers encapsulate how widget data is fetched. The package includes several built-in providers:

**EloquentDataProvider** — query Eloquent models with automatic date scoping:

```
EloquentDataProvider::for(Order::class)
    ->sum('total')
    ->dateColumn('created_at')
    ->scope('completed')
    ->query(fn ($query, $context) => $query->where('region', $context->filters['region'] ?? null));
```

**QueryBuilderDataProvider** — raw query builder for complex queries:

```
QueryBuilderDataProvider::for('analytics_events', 'analytics')
    ->count()
    ->dateColumn('occurred_at');
```

**CallbackDataProvider** — simple closure for quick widgets:

```
CallbackDataProvider::from(fn ($context) => WidgetData::stat(value: 42));
```

**ApiDataProvider** — fetch data from external APIs:

```
ApiDataProvider::from('https://api.example.com/metrics')
    ->headers(['Authorization' => 'Bearer ' . config('services.metrics.token')])
    ->timeout(10)
    ->transform(fn ($response, $context) => WidgetData::stat(value: $response['total']));
```

**RawSqlDataProvider** — escape hatch for raw SQL:

```
RawSqlDataProvider::from('SELECT COUNT(*) as total FROM orders WHERE created_at BETWEEN ? AND ?')
    ->bindingsFrom(fn ($context) => $context->dateRange());
```

### Periods and comparison

[](#periods-and-comparison)

Widgets automatically support period-based filtering and comparison. The available periods are:

- `today`, `7d`, `30d`, `90d`, `ytd`, `1y`, `custom`

Period comparison calculates the change between the current and previous period:

```
$data = WidgetData::stat(
    value: 1500,
    previousValue: 1200,
);

$data->change;          // 300
$data->changePercent;   // 25.0
$data->changeDirection; // ChangeDirection::POSITIVE
```

### Layouts and presets

[](#layouts-and-presets)

Each user gets their own dashboard layout stored in the database. Users can drag, drop, and resize widgets to customize their view.

**Presets** let administrators define reusable layouts:

```
// Create a system preset via artisan
php artisan dashboard:preset create --dashboard=main --name="Executive View" --system
```

Or programmatically:

```
use Reno\Dashboard\Actions\CreatePreset;
use Reno\Dashboard\Actions\ApplyPreset;

// Create a preset
$preset = (new CreatePreset())->execute($user, 'main', 'My Layout', $layoutArray);

// Apply a preset to a user's dashboard
(new ApplyPreset())->execute($user, $preset->id);
```

Layout resolution priority:

1. User's saved layout
2. Active preset layout
3. System preset layout
4. Widget default positions

### Authorization

[](#authorization)

The package includes a pluggable ACL system with three built-in drivers:

**Policy driver** (default) — uses each widget's `authorize()` method:

```
class RevenueWidget extends StatWidget
{
    public function authorize(?Authenticatable $user): bool
    {
        return $user?->hasRole('manager');
    }
}
```

**Spatie driver** — integrates with [spatie/laravel-permission](https://github.com/spatie/laravel-permission):

```
// config/dashboard.php
'acl' => [
    'driver' => 'spatie',
],

// Widget registration
Dashboard::widget('revenue')
    ->permissions(['view-revenue', 'access-dashboard'])
    ->register();
```

Sync permissions to the Spatie tables:

```
php artisan dashboard:permissions
```

**Custom driver** — implement your own:

```
// config/dashboard.php
'acl' => [
    'driver' => 'custom',
    'custom_driver' => App\Dashboard\MyAclDriver::class,
],
```

Dashboard-level authorization is handled by policies (`DashboardPolicy` and `PresetPolicy`) which control `view`, `editLayout`, `manage`, `create`, `update`, and `delete` actions.

### Caching

[](#caching)

Widget data is automatically cached to minimize expensive queries:

```
// config/dashboard.php
'cache' => [
    'enabled' => true,
    'store' => null,       // null = default store
    'prefix' => 'dashboard',
    'default_ttl' => 300,  // 5 minutes
    'tags_enabled' => false,
],
```

Per-widget cache control:

```
Dashboard::widget('expensive-report')
    ->cache(3600) // 1 hour
    ->register();
```

Warm the cache for all widgets:

```
php artisan dashboard:warm
php artisan dashboard:warm --dashboard=main --period=7d
```

The widget data API endpoints support ETag headers for efficient polling — clients receive `304 Not Modified` when data hasn't changed.

### Real-time updates

[](#real-time-updates)

The package supports four refresh strategies that control how widgets receive updated data:

StrategyDescription`poll`Default. Fetches widget data via HTTP on a timer (`setInterval` + fetch).`push`Listens for server-sent events via Laravel Echo (Reverb, Pusher, Ably, etc.).`inertia`Uses Inertia.js partial reloads to refresh widget props on a timer.`manual`No automatic refresh. Data is only loaded on initial mount or explicit call.#### Setting the refresh strategy per widget

[](#setting-the-refresh-strategy-per-widget)

Use the fluent builder or dedicated widget class methods:

```
// Poll every 30 seconds (default strategy)
Dashboard::widget('active-users')
    ->pollEvery(30)
    ->register();

// Push updates via broadcasting
Dashboard::widget('live-orders')
    ->pushUpdates()
    ->register();

// Inertia partial reload every 60 seconds
Dashboard::widget('revenue')
    ->inertiaPolling(60)
    ->register();

// Manual refresh only
Dashboard::widget('annual-report')
    ->manualRefresh()
    ->register();
```

Or use `refreshUsing()` with the `RefreshStrategy` enum for full control:

```
use Reno\Dashboard\Enums\RefreshStrategy;

Dashboard::widget('stats')
    ->refreshUsing(RefreshStrategy::INERTIA, interval: 45)
    ->register();
```

#### Global realtime adapter

[](#global-realtime-adapter)

The `realtime.adapter` config controls the default polling mechanism on the frontend. When set to `'inertia'`, all `poll` strategy widgets are automatically upgraded to use Inertia partial reloads instead of HTTP fetch:

```
// config/dashboard.php
'realtime' => [
    'adapter' => 'inertia', // 'fetch' (default) or 'inertia'
],
```

#### Push updates with broadcasting

[](#push-updates-with-broadcasting)

Enable broadcasting to push widget updates to connected clients via Laravel Echo:

```
// config/dashboard.php
'broadcasting' => [
    'enabled' => true,
    'channel_prefix' => 'dashboard',
],
```

The package dispatches three broadcast events:

- `WidgetDataUpdated` — when widget data changes
- `DashboardSaved` — when a user saves their layout
- `PresetApplied` — when a preset is applied

Trigger updates from your application code:

```
use Reno\Dashboard\Jobs\RefreshWidgetCache;

// Dispatch after an order is placed
RefreshWidgetCache::dispatch('revenue', 'main', '30d');
```

**Frontend setup** — the package reads `window.Echo` at runtime. Configure Laravel Echo in your app's bootstrap file as you normally would (the package does not bundle `laravel-echo`):

```
// resources/js/bootstrap.ts
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';

window.Echo = new Echo({
    broadcaster: 'reverb', // or 'pusher', 'ably', etc.
    // ...your config
});
```

All widgets on the same dashboard share a single channel subscription (`dashboard.{slug}`). The `useEcho` composable filters incoming events by `widget_key` so each widget only reacts to its own updates.

If `window.Echo` is not available, the composable logs a warning and the widget falls back gracefully (no crash).

#### Inertia polling adapter

[](#inertia-polling-adapter)

When a widget uses the `inertia` strategy (or the global adapter is set to `'inertia'`), the frontend uses `router.reload()` from `@inertiajs/vue3` with `only: ['widgets']` to perform a partial page reload. This is useful when you want to leverage Inertia's server-side data hydration instead of separate API calls.

The `@inertiajs/vue3` package is dynamically imported at runtime, so it's not required if you don't use this strategy.

#### Using the composables directly

[](#using-the-composables-directly)

The package exports `useEcho` and `useInertiaPolling` composables for advanced use cases:

```
import { useEcho, useInertiaPolling } from '@rjp2525/laravel-dashboards';

// Listen for push updates on a specific dashboard
const { connected, disconnect } = useEcho('main', 'dashboard', (widgetKey, data) => {
    console.log(`Widget ${widgetKey} updated:`, data);
});

// Start Inertia partial reloads every 30 seconds
const { start, stop } = useInertiaPolling(30);
start();
```

### Livewire integration

[](#livewire-integration)

For Livewire-based applications, the package includes two Livewire 3 components that provide the same widget rendering and real-time update capabilities without requiring Vue or Inertia.

**Requirements** — install Livewire 3 in your application:

```
composer require livewire/livewire "^3.0"
```

The package auto-detects Livewire and registers the components automatically. No manual registration is needed.

#### Full dashboard component

[](#full-dashboard-component)

Render an entire dashboard with all its authorized widgets:

```

```

#### Individual widget component

[](#individual-widget-component)

Render a single widget anywhere in your Blade templates:

```

```

#### Automatic polling

[](#automatic-polling)

Widgets using the `poll` strategy automatically include `wire:poll` with the configured interval. Push strategy widgets listen for Echo events via Livewire's `#[On]` attribute — updates broadcast on `dashboard.{slug}` are automatically received and filtered by widget key.

#### Publishing views

[](#publishing-views)

To customize the Livewire Blade templates:

```
php artisan vendor:publish --tag="dashboard-views"
```

This publishes the templates to `resources/views/vendor/dashboard/livewire/`.

### Exporting

[](#exporting)

Widgets can be exported to CSV:

```
// config/dashboard.php
'export' => [
    'enabled' => true,
    'formats' => ['csv'],
    'max_rows' => 10000,
],
```

The export adapts to the widget type — stat widgets export as a single row, chart widgets export series data, and table widgets export all rows.

### Multi-tenancy

[](#multi-tenancy)

For multi-tenant applications, the package can automatically scope widget data:

```
// config/dashboard.php
'tenancy' => [
    'enabled' => true,
    'resolver' => App\Dashboard\TenantResolver::class,
    'column' => 'tenant_id',
],
```

Data providers automatically filter by the resolved tenant when tenancy is enabled.

### API endpoints

[](#api-endpoints)

The package registers the following API routes (configurable prefix, default `api/dashboard`):

MethodURIDescription`GET``/widgets/{key}/data`Fetch widget data`POST``/widgets/batch`Fetch multiple widgets in one request`GET``/widgets/{key}/export`Export widget data`GET``/{slug}/layout`Load user layout`PUT``/{slug}/layout`Save user layout`GET``/{slug}/presets`List presets`POST``/{slug}/presets`Create preset`GET``/{slug}/presets/{id}`Show preset`PUT``/{slug}/presets/{id}`Update preset`DELETE``/{slug}/presets/{id}`Delete preset`POST``/{slug}/presets/{id}/apply`Apply presetA web route serves the Inertia dashboard page:

MethodURIDescription`GET``/dashboard/{slug?}`Dashboard page### Frontend

[](#frontend)

The package includes a Vue 3 + TypeScript frontend built with [GridStack.js](https://gridstackjs.com) for the grid layout and [ECharts](https://echarts.apache.org) for charts.

**Vue components:**

- `Dashboard` — root grid container
- `DashboardToolbar` — period selector, edit toggle, preset picker
- `WidgetWrapper` — widget container with header and error boundary
- `WidgetPicker` — sidebar to add widgets in edit mode
- `PresetManager` — save, load, and share presets
- `PeriodSelector` — date range picker
- `StatWidget`, `ChartWidget`, `PieChartWidget`, `TableWidget`, `ListWidget`, `ProgressWidget`, `CustomWidget`

**Composables:**

- `useDashboard()` — dashboard state, editing mode, layout management, `broadcastingEnabled` and `realtimeAdapter` refs
- `useWidget(definition)` — widget data, loading, error, refresh with automatic strategy resolution (poll/push/inertia/manual)
- `useWidgetData()` — data fetching with ETag support
- `useGridStack()` — GridStack initialization and events
- `usePeriod()` — period selection state
- `usePermissions()` — permission checks from Inertia shared data
- `useFetchClient()` — centralized fetch wrapper with XSRF injection and error interception
- `useEcho(slug, prefix, callback)` — Echo/Reverb/Pusher listener for push updates
- `useInertiaPolling(interval)` — Inertia.js partial reload polling

**Publishing and styling components:**

The package ships pre-built CSS and JS via `dist/`. Import the stylesheet in your application's entry point:

```
// resources/js/app.ts
import '@rjp2525/laravel-dashboards/dist/laravel-dashboards.css';
```

The default styles use CSS custom properties so you can override the theme without editing package files:

```
:root {
    --dashboard-bg: #ffffff;
    --dashboard-widget-bg: #f9fafb;
    --dashboard-widget-border: #e5e7eb;
    --dashboard-widget-radius: 0.5rem;
    --dashboard-widget-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
    --dashboard-text-primary: #111827;
    --dashboard-text-secondary: #6b7280;
    --dashboard-accent: #3b82f6;
    --dashboard-positive: #10b981;
    --dashboard-negative: #ef4444;
}
```

For dark mode, the package respects `prefers-color-scheme: dark` automatically when the config theme is set to `auto`, or you can force it:

```
// config/dashboard.php
'frontend' => [
    'theme' => 'dark', // auto, light, dark
],
```

#### Error handling

[](#error-handling)

All internal fetch calls (layout saves, preset operations, widget data) go through a centralized fetch client. You can register global error handlers using `onFetchError` to hook into failures — for example, to show toast notifications or report to an error tracking service:

```
import { onFetchError } from '@rjp2525/laravel-dashboards'

// Register a global error handler (e.g. in app.ts)
const unsubscribe = onFetchError((context) => {
    console.error(`Dashboard API error: ${context.method} ${context.url} — ${context.status}`)
    toast.error(`Dashboard error: ${context.statusText}`)
})

// Optionally unsubscribe later
unsubscribe()
```

The `context` object passed to handlers has the following shape:

```
interface FetchErrorContext {
    url: string        // Full request URL
    method: string     // HTTP method (GET, POST, PUT, DELETE)
    status?: number    // HTTP status code
    statusText?: string // HTTP status text
    body?: unknown     // Parsed JSON error body (when available)
    error: Error       // The Error instance that will be thrown
}
```

Handlers are called before the error is thrown, so individual callers can still catch errors locally if needed. Multiple handlers can be registered and each receives the same context.

To register a custom Vue widget component, use the `CustomWidget` type and point to your component:

```
Dashboard::widget('my-custom')
    ->label('My Custom Widget')
    ->type(WidgetType::CUSTOM)
    ->component('MyCustomWidget')
    ->using(fn ($context) => WidgetData::stat(value: 42))
    ->register();
```

Then register the component in your Vue app:

```
import MyCustomWidget from './components/MyCustomWidget.vue';

app.component('MyCustomWidget', MyCustomWidget);
```

Your custom component receives `widget` (definition) and `data` (WidgetData) as props.

### Configuration

[](#configuration)

The full config file (`config/dashboard.php`) covers:

- **Routing** — prefix, middleware, domain
- **Grid** — columns, row height, margin, animation, drag/resize toggles
- **ACL** — driver selection and custom driver class
- **Cache** — store, prefix, TTL, tag support
- **Broadcasting** — enabled, channel prefix, driver
- **Realtime** — frontend polling adapter (`fetch` or `inertia`)
- **Periods** — default period and available options
- **Export** — enabled, formats, max rows
- **Frontend** — chart adapter (`echarts`, `apexcharts`, `chartjs`), theme, locale
- **Presets** — user presets toggle, max per user
- **Tenancy** — enabled, resolver, column
- **Discovery** — attribute scanning toggle and directory paths

Testing
-------

[](#testing)

```
composer test
```

Changelog
---------

[](#changelog)

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

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

[](#contributing)

Please see [CONTRIBUTING](https://github.com/rjp2525/.github/blob/main/CONTRIBUTING.md) for details.

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

[](#security-vulnerabilities)

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

Credits
-------

[](#credits)

- [Reno Philibert](https://github.com/rjp2525)
- [All Contributors](../../contributors)

License
-------

[](#license)

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

###  Health Score

37

—

LowBetter than 81% of packages

Maintenance81

Actively maintained with recent releases

Popularity5

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 62.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 ~0 days

Total

5

Last Release

130d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/fce8bcb31baa82e9bbb79d1ea08d167125d3d0a2d9d269af1b293b7d0d8159ba?d=identicon)[rjp2525](/maintainers/rjp2525)

---

Top Contributors

[![rjp2525](https://avatars.githubusercontent.com/u/1334865?v=4)](https://github.com/rjp2525 "rjp2525 (5 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (3 commits)")

---

Tags

laravelinertiadashboardwidgetsgridstackvuerjp2525laravel-dashboards

###  Code Quality

TestsPest

Static AnalysisPHPStan, Rector

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/rjp2525-laravel-dashboards/health.svg)

```
[![Health](https://phpackages.com/badges/rjp2525-laravel-dashboards/health.svg)](https://phpackages.com/packages/rjp2525-laravel-dashboards)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3355.3M346](/packages/psalm-plugin-laravel)[api-platform/laravel

API Platform support for Laravel

58171.5k14](/packages/api-platform-laravel)[laravel/cashier

Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.

2.6k29.9M146](/packages/laravel-cashier)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k15.1M132](/packages/laravel-pulse)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

77022.3M151](/packages/laravel-mcp)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9762.4M131](/packages/roots-acorn)

PHPackages © 2026

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