spatie/laravel-dashboard
Build beautiful, Livewire-powered dashboards in Laravel. Provides base CSS, dashboard and tile view components, and a Tile model to persist fetched data so tiles can update themselves via polling.
Installation:
composer require spatie/laravel-dashboard
Publish the package assets and config:
php artisan vendor:publish --provider="Spatie\Dashboard\DashboardServiceProvider"
First Dashboard:
Create a Livewire component (e.g., php artisan make:livewire MyDashboardTile) and register it as a tile:
// app/Providers/AppServiceProvider.php
use Spatie\Dashboard\Dashboard;
public function boot()
{
Dashboard::tile('my-dashboard-tile', \App\Livewire\MyDashboardTile::class);
}
Display the Dashboard:
Add the dashboard component to your layout (e.g., resources/views/layouts/app.blade.php):
<x-dashboard>
@dashboard
</x-dashboard>
First Tile:
Create a simple Livewire component (MyDashboardTile) with a basic view:
<!-- resources/views/livewire/my-dashboard-tile.blade.php -->
<div>
<h3>My Tile</h3>
<p>Hello, Dashboard!</p>
</div>
Use the Tile model to cache data and enable polling:
// In your Livewire component
use Spatie\Dashboard\Tile;
public function mount()
{
$this->tile = Tile::firstOrCreate(
name: 'user-metrics',
component: self::class
);
}
public function render()
{
$data = $this->fetchData(); // Your logic to fetch data
$this->tile->data = $data;
$this->tile->save();
return view('livewire.my-dashboard-tile', ['data' => $data]);
}
public function fetchData()
{
return User::count(); // Example: Fetch user count
}
Tile Registration:
Register tiles in AppServiceProvider or a dedicated service provider:
Dashboard::tile('analytics', \App\Livewire\AnalyticsTile::class);
Dashboard::tile('recent-activity', \App\Livewire\RecentActivityTile::class);
Dynamic Tile Loading: Use conditional registration for admin-only tiles:
if (auth()->user()->isAdmin()) {
Dashboard::tile('admin-stats', \App\Livewire\AdminStatsTile::class);
}
Tile Groups: Organize tiles into logical groups (e.g., "Analytics", "Users") using Blade sections:
<x-dashboard>
<x-slot name="analytics">
@dashboard('analytics')
</x-slot>
<x-slot name="users">
@dashboard('users')
</x-slot>
</x-dashboard>
Livewire Polling:
Enable automatic updates via Livewire's poll method:
protected $listeners = ['refresh' => 'fetchData'];
public function mount()
{
$this->poll(60000); // Refresh every 60 seconds
}
Data Persistence:
Use the Tile model to store fetched data (e.g., for offline access or performance):
$tile = Tile::firstOrNew(['name' => 'user-stats']);
$tile->data = ['count' => User::count()];
$tile->save();
Custom Styling: Override the default CSS by publishing and extending the package styles:
php artisan vendor:publish --tag=laravel-dashboard-public
Then customize resources/css/dashboard.css.
Authentication: Protect the dashboard with middleware:
Route::middleware(['auth'])->group(function () {
Route::get('/dashboard', function () {
return view('dashboard');
});
});
Multi-Tenant Support:
Scope tiles to tenants using the tenant method:
Dashboard::tile('tenant-stats', \App\Livewire\TenantStatsTile::class)
->tenant(Tenant::class);
Livewire Component Naming: Ensure Livewire component class names match the registered tile names (case-sensitive). Example:
Dashboard::tile('user-metrics', \App\Livewire\UserMetrics::class); // Correct
Dashboard::tile('user-metrics', \App\Livewire\Usermetrics::class); // Fails silently
Polling Conflicts: Avoid registering multiple tiles with the same name, as it can cause Livewire to load the wrong component.
Data Serialization:
The Tile::data field uses JSON encoding. Ensure your stored data is serializable:
// Bad: Non-serializable object
$tile->data = new DateTime();
// Good: Convert to string or array
$tile->data = ['last_updated' => now()->toIso8601String()];
Caching Headaches: Clear cached views and configurations after publishing assets:
php artisan view:clear
php artisan config:clear
Tile Not Rendering: Check if the tile is registered correctly:
// Dump registered tiles
dd(Dashboard::tiles());
Livewire Updates Not Triggering:
Verify the poll method is called in mount() and the component is properly registered.
CSS Overrides Not Applying: Ensure your custom CSS is loaded after the package CSS in your layout:
@vite(['resources/css/app.css', 'resources/css/dashboard.css'])
Reusable Tile Logic: Extract common tile functionality into a base Livewire component:
class BaseDashboardTile extends Component
{
use WithTileData; // Hypothetical trait for shared logic
public function fetchData()
{
// Shared logic (e.g., caching, error handling)
}
}
Tile Configuration:
Pass configuration via the config method:
Dashboard::tile('settings', \App\Livewire\SettingsTile::class)
->config(['poll_interval' => 30000]); // Custom poll interval
Dark Mode Support: Use Tailwind’s dark mode classes or extend the package’s CSS for dark mode:
@layer components.dashboard {
.dark .dashboard-tile {
background: #1a1a1a;
}
}
Testing: Test tiles in isolation using Livewire’s testing helpers:
$this->livewire(MyDashboardTile::class)
->assertSee('Hello, Dashboard!');
Performance:
Lazy-load tiles or use defer for non-critical components:
@dashboard('analytics', defer: true)
How can I help you explore Laravel packages today?