Live Dashboard
This recipe demonstrates building a dashboard with real-time data updates using Datastar's interval observer to periodically fetch fresh data from the server.
What We're Building
A system monitoring dashboard with:
- Auto-refresh every 5 seconds
- Manual refresh button
- Pause/resume functionality
- Multiple metric cards
- Last updated timestamp
Complete Implementation
Blade Template
Create resources/views/dashboard/index.blade.php:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Live Dashboard</title>
@hyper
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-50">
<div class="max-w-7xl mx-auto px-4 py-8">
<div @signals([
'cpuUsage' => $metrics['cpuUsage'],
'memoryUsage' => $metrics['memoryUsage'],
'activeUsers' => $metrics['activeUsers'],
'requestsPerMinute' => $metrics['requestsPerMinute'],
'lastUpdated' => now()->format('H:i:s'),
'_paused' => false
])>
<!-- Header -->
<div class="flex justify-between items-center mb-8">
<h1 class="text-3xl font-bold">System Dashboard</h1>
<div class="flex gap-3">
<button
data-on:click="$_paused = !$_paused"
data-class:bg-blue-600="!$_paused"
data-class:bg-gray-600="$_paused"
class="px-4 py-2 text-white rounded-md hover:opacity-90 transition-colors">
<span data-show="!$_paused">⏸ Pause</span>
<span data-show="$_paused">▶ Resume</span>
</button>
<button
data-on:click="@get('/dashboard/refresh?manual=1')"
class="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors">
🔄 Refresh Now
</button>
</div>
</div>
<!-- Status Indicator -->
<div class="mb-6 flex items-center gap-4">
<div class="flex items-center gap-2">
<div
data-class:bg-green-500="!$_paused"
data-class:bg-gray-400="$_paused"
class="w-3 h-3 rounded-full animate-pulse">
</div>
<p class="text-sm text-gray-600">
<span data-show="!$_paused" class="text-green-600 font-medium">Auto-refresh active (every 5s)</span>
<span data-show="$_paused" class="text-gray-500 font-medium">Auto-refresh paused</span>
</p>
</div>
<p class="text-sm text-gray-500">
Last updated: <span data-text="$lastUpdated" class="font-mono"></span>
</p>
</div>
<!-- Auto-refresh interval (only when not paused) -->
<div>
<div data-on-interval__duration.5s="if (!$_paused) { @get('/dashboard/refresh') }"></div>
</div>
<!-- Metrics Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<!-- CPU Usage Card -->
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-gray-500 text-sm font-medium">CPU Usage</h3>
<svg class="w-8 h-8 text-blue-500" fill="currentColor" viewBox="0 0 20 20">
<path d="M13 7H7v6h6V7z"></path>
<path fill-rule="evenodd" d="M7 2a1 1 0 012 0v1h2V2a1 1 0 112 0v1h2a2 2 0 012 2v2h1a1 1 0 110 2h-1v2h1a1 1 0 110 2h-1v2a2 2 0 01-2 2h-2v1a1 1 0 11-2 0v-1H9v1a1 1 0 11-2 0v-1H5a2 2 0 01-2-2v-2H2a1 1 0 110-2h1V9H2a1 1 0 010-2h1V5a2 2 0 012-2h2V2z"></path>
</svg>
</div>
<p class="text-3xl font-bold text-gray-900 mb-2" data-text="$cpuUsage + '%'"></p>
<div class="w-full bg-gray-200 rounded-full h-2">
<div
data-attr:style="'width: ' + $cpuUsage + '%'"
data-class:bg-red-500="$cpuUsage > 80"
data-class:bg-yellow-500="$cpuUsage > 50 && $cpuUsage <= 80"
data-class:bg-green-500="$cpuUsage <= 50"
class="h-2 rounded-full transition-all duration-500">
</div>
</div>
</div>
<!-- Memory Usage Card -->
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-gray-500 text-sm font-medium">Memory Usage</h3>
<svg class="w-8 h-8 text-purple-500" fill="currentColor" viewBox="0 0 20 20">
<path d="M3 12v3c0 1.657 3.134 3 7 3s7-1.343 7-3v-3c0 1.657-3.134 3-7 3s-7-1.343-7-3z"></path>
<path d="M3 7v3c0 1.657 3.134 3 7 3s7-1.343 7-3V7c0 1.657-3.134 3-7 3S3 8.657 3 7z"></path>
<path d="M17 5c0 1.657-3.134 3-7 3S3 6.657 3 5s3.134-3 7-3 7 1.343 7 3z"></path>
</svg>
</div>
<p class="text-3xl font-bold text-gray-900 mb-2" data-text="$memoryUsage + '%'"></p>
<div class="w-full bg-gray-200 rounded-full h-2">
<div
data-attr:style="'width: ' + $memoryUsage + '%'"
data-class:bg-red-500="$memoryUsage > 80"
data-class:bg-yellow-500="$memoryUsage > 50 && $memoryUsage <= 80"
data-class:bg-green-500="$memoryUsage <= 50"
class="h-2 rounded-full transition-all duration-500">
</div>
</div>
</div>
<!-- Active Users Card -->
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-gray-500 text-sm font-medium">Active Users</h3>
<svg class="w-8 h-8 text-green-500" fill="currentColor" viewBox="0 0 20 20">
<path d="M9 6a3 3 0 11-6 0 3 3 0 016 0zM17 6a3 3 0 11-6 0 3 3 0 016 0zM12.93 17c.046-.327.07-.66.07-1a6.97 6.97 0 00-1.5-4.33A5 5 0 0119 16v1h-6.07zM6 11a5 5 0 015 5v1H1v-1a5 5 0 015-5z"></path>
</svg>
</div>
<p class="text-3xl font-bold text-gray-900" data-text="$activeUsers"></p>
<p class="text-sm text-gray-500">Currently online</p>
</div>
<!-- Requests Per Minute Card -->
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-gray-500 text-sm font-medium">Requests/Min</h3>
<svg class="w-8 h-8 text-yellow-500" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M12 7a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0V8.414l-4.293 4.293a1 1 0 01-1.414 0L8 10.414l-4.293 4.293a1 1 0 01-1.414-1.414l5-5a1 1 0 011.414 0L11 10.586 14.586 7H12z"></path>
</svg>
</div>
<p class="text-3xl font-bold text-gray-900" data-text="$requestsPerMinute"></p>
<p class="text-sm text-gray-500">Average throughput</p>
</div>
</div>
</div>
</div>
</body>
</html>Controller
Create app/Http/Controllers/DashboardController.php:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class DashboardController extends Controller
{
public function index()
{
$metrics = $this->getMetrics();
return view('dashboard.index', compact('metrics'));
}
public function refresh(Request $request)
{
$metrics = $this->getMetrics();
return hyper()->signals([
'cpuUsage' => $metrics['cpuUsage'],
'memoryUsage' => $metrics['memoryUsage'],
'activeUsers' => $metrics['activeUsers'],
'requestsPerMinute' => $metrics['requestsPerMinute'],
'lastUpdated' => now()->format('H:i:s')
]);
}
protected function getMetrics(): array
{
return [
'cpuUsage' => Cache::remember('cpu_usage', 5, fn() => rand(20, 95)),
'memoryUsage' => Cache::remember('memory_usage', 5, fn() => rand(30, 85)),
'activeUsers' => Cache::remember('active_users', 5, fn() => rand(10, 150)),
'requestsPerMinute' => Cache::remember('requests_per_minute', 5, fn() => rand(50, 500))
];
}
}How It Works
Automatic Refresh
Datastar's data-on-interval triggers periodic updates:
<div>
<div data-on-interval__duration.5s="if (!$_paused) { @get('/dashboard/refresh') }"></div>
</div>Breakdown:
data-on-interval(Datastar) - Repeats action at intervals__5s(Datastar) - Every 5 seconds@get(...)(Datastar) - Fetches updated metricsif (!$_paused)- Only active when not paused
Pause/Resume
A local signal _paused controls auto-refresh:
<button data-on:click="$_paused = !$_paused">
<span data-text="$_paused ? 'Resume' : 'Pause'"></span>
</button>When _paused is true, auto-refreshing is stopped .
Dynamic Progress Bars
Progress bars use data-attr:style (Datastar) for width and conditional classes for color:
<div
data-attr:style="'width: ' + $cpuUsage + '%'"
data-class:bg-red-500="$cpuUsage > 80"
data-class:bg-yellow-500="$cpuUsage > 50 && $cpuUsage <= 80"
data-class:bg-green-500="$cpuUsage <= 50"
class="h-2 rounded-full">
</div>The bar width and color update reactively as cpuUsage changes.
Key Takeaways
1. Interval Observer:data-on-interval (Datastar) simplifies periodic polling without managing setInterval manually.
2. Conditional Intervals: Using data-show to control interval elements allows pause/resume functionality.
3. Local Signals for UI State: The _paused signal (Datastar local signal) manages UI state without server communication.
Enhancements
Alert Thresholds
Add visual alerts for critical values:
<div data-show="$cpuUsage > 90" class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 mb-6">
⚠️ Critical: CPU usage is above 90%
</div>
