A custom Filament dashboard that transforms the live USGS earthquake feed into statistics, a magnitude chart, and an actionable recent-earthquakes table without storing data locally.
One-time payment
Sign in with GitHub to buy
Sign in first, then complete your $9 checkout.
30-day money-back guarantee
This project demonstrates how to build a custom Filament dashboard powered by an external API instead of Eloquent models. It loads the USGS real-time earthquake feed and turns the latest events into summary statistics, a magnitude chart, and a paginated table with links to the original USGS event pages.
The key idea is that every widget reads from one scoped EarthquakeFeed service. The service fetches and normalizes the GeoJSON response once per request, so the stats, chart, and table can share the same data without making three separate API calls. The dashboard also handles an unavailable feed gracefully instead of breaking the whole panel.
The repository contains the complete Laravel + Filament project to demonstrate the functionality, including a seeded administrator account and tests for the API service and dashboard widgets.
The Filament project is in the app/Filament folder.
Feel free to pick the parts that you actually need in your projects.
git clone.env.example file to .env and edit database credentials there (the default is SQLite)composer installphp artisan key:generatephp artisan storage:linkphp artisan migrate --seed/admin and log in with credentials [email protected] and password

The application is organized around one custom Filament dashboard. The page decides which widgets appear and how they are laid out, while a small service fetches the live data that every widget presents in a different way.
The custom dashboard extends Filament's base dashboard page. It changes the page title and subheading, explicitly returns the three earthquake widgets in display order, and uses a two-column grid. The chart and table set their own column span to full, so only the summary stats use the multi-column layout.
app/Filament/Pages/Dashboard.php
class Dashboard extends BaseDashboard{ protected static ?string $title = 'Earthquake Monitor'; public function getWidgets(): array { return [ EarthquakeStatsOverview::class, EarthquakeMagnitudeChart::class, RecentEarthquakesTable::class, ]; } public function getColumns(): int|array { return 2; } public function getSubheading(): ?string { return 'Source: USGS real-time earthquake feed'; }}
The custom page replaces the default dashboard by being registered in the panel provider:
app/Providers/Filament/AdminPanelProvider.php
->pages([ Dashboard::class,])
The key points:
getColumns() creates the two-column page grid, while widgets that need more horizontal space use protected int|string|array $columnSpan = 'full'EarthquakeFeed calls the USGS all_day.geojson endpoint with short connection and response timeouts. It validates each feature, converts the millisecond timestamp into a CarbonImmutable instance, extracts coordinates and depth, and limits the result to 100 records. Connection errors, unsuccessful responses, and unexpected payloads become an unavailable EarthquakeFeedResult instead of escaping into the widgets.
app/Services/EarthquakeFeed.php
public function get(): EarthquakeFeedResult{ return $this->result ??= $this->fetch();} private function fetch(): EarthquakeFeedResult{ try { $response = Http::timeout(5) ->connectTimeout(3) ->get(self::ENDPOINT); $response->throw(); } catch (ConnectionException|RequestException $exception) { return EarthquakeFeedResult::unavailable($exception->getMessage()); } // Normalize the USGS GeoJSON features...}
The service is registered as scoped, and its get() method memoizes the result. This lets every widget resolve the same service and reuse one API response during the request.