rawand201 / filament-spatie-lighthouse
A Filament plugin for running and viewing Google Lighthouse audit results
Package info
github.com/RawanD201/filament-spatie-lighthouse
pkg:composer/rawand201/filament-spatie-lighthouse
Requires
- php: ^8.2
- filament/filament: ^4.0|^5.0
- spatie/laravel-package-tools: ^1.9
- spatie/lighthouse-php: ^2.0
Requires (Dev)
- orchestra/testbench: ^9.0
- pestphp/pest: ^2.0
- pestphp/pest-plugin-laravel: ^2.0
README
A Filament plugin that integrates spatie/lighthouse-php to run Google Lighthouse audits directly from your admin panel — view scores, performance metrics, failed audits, audit history, and full HTML reports without leaving Filament.
Table of Contents
- Requirements
- Installation
- Basic Usage
- Plugin Options
- Configuration Reference
- Raw Results Storage
- Queue Support
- Scheduled Audits
- Notifications
- REST API Endpoints
- Artisan Commands
- Events
- Custom Result Store
- Extending the Page
- Contributing
- Credits
- Changelog
- License
Requirements
| Requirement | Version |
|---|---|
| PHP | ^8.2 |
| Laravel | ^11.0 | ^12.0 |
| Filament | ^4.0 | ^5.0 |
| spatie/lighthouse-php | ^2.0 |
| Node.js | any recent LTS |
| Chrome / Chromium | any recent version |
Note:
spatie/lighthouse-phprequires Node.js and a Chromium-based browser installed on the server running audits. See their documentation for setup.
Installation
1. Install the package:
composer require rawand201/filament-spatie-lighthouse
2. Publish and run the migration:
php artisan vendor:publish --tag="filament-spatie-lighthouse-migrations"
php artisan migrate
3. (Optional) Publish the config file:
php artisan vendor:publish --tag="filament-spatie-lighthouse-config"
4. Register the plugin in your Filament panel provider:
use FilamentSpatieLighthouse\FilamentSpatieLighthousePlugin; public function panel(Panel $panel): Panel { return $panel // ... ->plugin(FilamentSpatieLighthousePlugin::make()); }
That's it. A "Lighthouse Results" page will appear in your panel's navigation.
Basic Usage
Once installed, navigate to the Lighthouse Results page in your Filament panel. Enter a URL and click Run Audit. The plugin will:
- Run a Lighthouse audit via
spatie/lighthouse-php - Save the results (scores + raw data)
- Display category scores, performance metrics, and failed audits
Each row in the results table has actions to:
- View the full result detail page
- View HTML Report — opens the native Lighthouse HTML report inline
- Download HTML Report — downloads the report as a
.htmlfile - Delete the record
Plugin Options
Customise the plugin when registering it in your panel provider:
FilamentSpatieLighthousePlugin::make() ->navigationGroup('Tools') // set or override the navigation group ->navigationLabel('Lighthouse') // set the nav item label ->navigationIcon('heroicon-o-bolt') // set the nav item icon ->navigationSort(5) // set the sort order ->authorize(fn (): bool => auth()->user()->can('view lighthouse'));
All available methods
| Method | Type | Default | Description |
|---|---|---|---|
navigationGroup(string|Closure|null) |
static |
translation key | Navigation group label |
navigationLabel(string|Closure|null) |
static |
translation key | Nav item label |
navigationIcon(string|Closure) |
static |
heroicon-o-chart-bar |
Nav item icon |
navigationSort(int|Closure) |
static |
1 |
Sort order within group |
authorize(bool|Closure) |
static |
true |
Access control callback |
usingPage(string) |
static |
LighthouseResults::class |
Custom page class |
Configuration Reference
After publishing, edit config/filament-spatie-lighthouse.php. Below is the full reference:
Result Store
// Where to store audit metadata (scores, timestamps, etc.) // Options: 'database' (default) | 'cache' 'result_store' => env('LIGHTHOUSE_RESULT_STORE', 'database'),
Raw Results Storage
Controls where the large raw Lighthouse JSON (500KB–2MB per audit) is saved:
// Options: 'database' (default) | 'filesystem' 'raw_results_driver' => env('LIGHTHOUSE_RAW_RESULTS_DRIVER', 'database'), // Laravel disk to use when driver is 'filesystem' 'raw_results_disk' => env('LIGHTHOUSE_RAW_RESULTS_DISK', 'local'), // Directory within the disk 'raw_results_path' => env('LIGHTHOUSE_RAW_RESULTS_PATH', 'lighthouse-results'),
See Raw Results Storage for details.
Database
'database' => [ // Use a specific DB connection (null = app default) 'connection' => env('LIGHTHOUSE_DB_CONNECTION', null), ],
Cache
// TTL in seconds when using the 'cache' result store 'cache_ttl' => env('LIGHTHOUSE_CACHE_TTL', 86400), // 24 hours
Audit Timeout
// Lighthouse audit timeout in seconds // When running synchronously, PHP's max_execution_time must be higher than this value 'default_timeout' => env('LIGHTHOUSE_TIMEOUT', 180),
History Retention
// Auto-prune records older than N days (database store only, uses MassPrunable) // Run: php artisan model:prune --model=FilamentSpatieLighthouse\\Models\\LighthouseAuditResult 'keep_history_for_days' => env('LIGHTHOUSE_KEEP_HISTORY_DAYS', 30),
Default Audit Categories
'default_categories' => [ 'performance', 'accessibility', 'best-practices', 'seo', ],
Queue
'use_queue' => env('LIGHTHOUSE_USE_QUEUE', false), 'queue_connection' => env('LIGHTHOUSE_QUEUE_CONNECTION', null), 'queue_name' => env('LIGHTHOUSE_QUEUE_NAME', 'default'), 'queue_tries' => env('LIGHTHOUSE_QUEUE_TRIES', 1), 'queue_timeout' => env('LIGHTHOUSE_QUEUE_TIMEOUT', 300),
Score Thresholds
Controls the colour indicators on score badges:
'score_thresholds' => [ 'good' => 90, // >= 90 → green 'needs_improvement' => 50, // >= 50 → orange, < 50 → red ],
Metric Thresholds
Controls the colour indicators on individual performance metrics:
'metric_thresholds' => [ 'first_contentful_paint' => ['good' => 1800, 'needs_improvement' => 3000, 'unit' => 'ms'], 'largest_contentful_paint'=> ['good' => 2500, 'needs_improvement' => 4000, 'unit' => 'ms'], 'speed_index' => ['good' => 3400, 'needs_improvement' => 5800, 'unit' => 'ms'], 'total_blocking_time' => ['good' => 200, 'needs_improvement' => 600, 'unit' => 'ms'], 'time_to_interactive' => ['good' => 3800, 'needs_improvement' => 7300, 'unit' => 'ms'], 'cumulative_layout_shift' => ['good' => 0.1, 'needs_improvement' => 0.25, 'unit' => 'score'], ],
Display Options
Fine-grained control over which UI sections are shown:
'display' => [ 'show_category_scores' => true, 'show_audit_info' => true, 'show_html_report' => true, 'show_performance_metrics' => true, 'show_failed_audits' => true, 'show_history' => true, // Number of failed audits shown before a "Show All" toggle 'failed_audits_initial_count' => 10, // Max height of the failed audits scrollable container (CSS value) 'failed_audits_max_height' => '800px', // How many history records to show per URL 'history_count' => 5, // Table auto-refresh interval (null to disable) 'table_poll_interval' => '30s', // Toggle individual table row actions 'table_actions' => [ 'view' => true, 'view_html' => true, 'download_html' => true, 'delete' => true, ], ],
Export
'export' => [ 'enabled' => env('LIGHTHOUSE_EXPORT_ENABLED', true), 'formats' => ['csv', 'json'], 'disk' => env('LIGHTHOUSE_EXPORT_DISK', 'local'), 'path' => env('LIGHTHOUSE_EXPORT_PATH', 'lighthouse-exports'), ],
Scheduling
'scheduling' => [ 'enabled' => env('LIGHTHOUSE_SCHEDULING_ENABLED', true), 'default_frequency' => env('LIGHTHOUSE_SCHEDULE_FREQUENCY', 'daily'), // List of URLs to audit on a schedule 'urls' => [ // Simple entry — uses default_frequency and desktop form factor ['url' => 'https://example.com'], // Full entry [ 'url' => 'https://example.com/blog', 'frequency' => 'weekly', // 'hourly' | 'daily' | 'weekly' 'form_factor' => 'mobile', // 'desktop' | 'mobile' ], ], ],
Notifications
'notifications' => [ 'email' => [ 'enabled' => env('LIGHTHOUSE_NOTIFICATIONS_EMAIL_ENABLED', false), 'to' => env('LIGHTHOUSE_NOTIFICATIONS_EMAIL_TO', null), 'on_failure' => env('LIGHTHOUSE_NOTIFICATIONS_EMAIL_ON_FAILURE', true), 'on_completion' => env('LIGHTHOUSE_NOTIFICATIONS_EMAIL_ON_COMPLETION', false), ], 'slack' => [ 'enabled' => env('LIGHTHOUSE_NOTIFICATIONS_SLACK_ENABLED', false), 'webhook_url' => env('LIGHTHOUSE_NOTIFICATIONS_SLACK_WEBHOOK_URL', null), 'channel' => env('LIGHTHOUSE_NOTIFICATIONS_SLACK_CHANNEL', null), 'on_failure' => env('LIGHTHOUSE_NOTIFICATIONS_SLACK_ON_FAILURE', true), 'on_completion' => env('LIGHTHOUSE_NOTIFICATIONS_SLACK_ON_COMPLETION', false), ], ],
REST API Endpoints
'endpoints' => [ 'enabled' => env('LIGHTHOUSE_ENDPOINTS_ENABLED', false), 'secret_token' => env('LIGHTHOUSE_ENDPOINTS_SECRET_TOKEN', null), 'prefix' => env('LIGHTHOUSE_ENDPOINTS_PREFIX', 'lighthouse-api'), ],
Raw Results Storage
Each Lighthouse audit produces a raw JSON result of 500KB–2MB. By default this is stored in a raw_results database column. For production deployments with frequent audits, this can bloat your database significantly.
The plugin supports opt-in filesystem storage — the raw JSON is written to disk (or any Laravel filesystem disk including S3) and only the file path is stored in the database.
Enable filesystem storage
In .env:
LIGHTHOUSE_RAW_RESULTS_DRIVER=filesystem LIGHTHOUSE_RAW_RESULTS_DISK=local # or s3, public, etc. LIGHTHOUSE_RAW_RESULTS_PATH=lighthouse-results
Or in config/filament-spatie-lighthouse.php:
'raw_results_driver' => 'filesystem', 'raw_results_disk' => 's3', 'raw_results_path' => 'lighthouse/raw',
How it works
| Mode | raw_results column |
raw_result_path column |
|---|---|---|
database (default) |
Full JSON blob | null |
filesystem |
null |
Path on disk (e.g. lighthouse-results/abc123-2025-01-01.json) |
The HTML report view and download actions transparently resolve the raw results from either location — no change is needed to how you use the plugin.
Tip: You can mix both modes in the same database — existing records with
raw_resultscontinue to work after you switch tofilesystem.
Queue Support
Running Lighthouse synchronously blocks the web request for up to 3 minutes. For production use, queue audits in a background worker.
Enable queue mode in .env:
LIGHTHOUSE_USE_QUEUE=true LIGHTHOUSE_QUEUE_CONNECTION=redis # optional, uses default queue connection if omitted LIGHTHOUSE_QUEUE_NAME=lighthouse # optional, uses 'default' queue if omitted LIGHTHOUSE_QUEUE_TIMEOUT=300 # must be > LIGHTHOUSE_TIMEOUT LIGHTHOUSE_QUEUE_TRIES=1
Then run a queue worker:
php artisan queue:work --queue=lighthouse --timeout=300
When queue mode is enabled, clicking "Run Audit" in the UI dispatches a RunLighthouseAuditJob. The table polls every 30 seconds by default (configurable via display.table_poll_interval) and updates when the job completes.
Dispatching manually
use FilamentSpatieLighthouse\Jobs\RunLighthouseAuditJob; RunLighthouseAuditJob::dispatch( url: 'https://example.com', categories: ['performance', 'seo'], formFactor: 'mobile', userAgent: 'MyBot/1.0', headers: ['Authorization' => 'Bearer secret'], throttleCpu: true, throttleNetwork: true, timeoutSeconds: 180, userId: (string) auth()->id(), );
Scheduled Audits
The plugin registers its own schedule using Laravel's Schedule facade — you do not need to modify your app/Console/Kernel.php.
Configure URLs to audit on a schedule:
'scheduling' => [ 'enabled' => true, 'default_frequency' => 'daily', 'urls' => [ ['url' => 'https://example.com'], ['url' => 'https://example.com/shop', 'frequency' => 'hourly'], ['url' => 'https://example.com/blog', 'frequency' => 'weekly', 'form_factor' => 'mobile'], ], ],
Make sure the Laravel scheduler is running:
# crontab * * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
Supported frequencies: hourly, daily, weekly.
You can also dispatch a one-off scheduled job from the command line:
php artisan lighthouse:schedule https://example.com --form-factor=mobile
Notifications
The plugin can send notifications when an audit completes or fails.
LIGHTHOUSE_NOTIFICATIONS_EMAIL_ENABLED=true LIGHTHOUSE_NOTIFICATIONS_EMAIL_TO=team@example.com LIGHTHOUSE_NOTIFICATIONS_EMAIL_ON_FAILURE=true LIGHTHOUSE_NOTIFICATIONS_EMAIL_ON_COMPLETION=false
The email includes the URL, all four category scores, and a timestamp.
Slack
LIGHTHOUSE_NOTIFICATIONS_SLACK_ENABLED=true LIGHTHOUSE_NOTIFICATIONS_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/... LIGHTHOUSE_NOTIFICATIONS_SLACK_CHANNEL=#lighthouse LIGHTHOUSE_NOTIFICATIONS_SLACK_ON_FAILURE=true LIGHTHOUSE_NOTIFICATIONS_SLACK_ON_COMPLETION=false
The Slack attachment is colour-coded based on the performance score (green ≥ 90, orange ≥ 50, red < 50).
REST API Endpoints
The plugin exposes optional read-only HTTP endpoints for fetching audit results programmatically.
Enable in config:
LIGHTHOUSE_ENDPOINTS_ENABLED=true LIGHTHOUSE_ENDPOINTS_SECRET_TOKEN=your-secret-token LIGHTHOUSE_ENDPOINTS_PREFIX=lighthouse-api
Authenticate requests by passing the token in a header or query string:
X-Lighthouse-Token: your-secret-token
# or
?token=your-secret-token
Available endpoints
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/{prefix}/health |
No | Health check |
GET |
/{prefix}/latest?url=... |
Yes | Latest result for a URL |
GET |
/{prefix}/results |
Yes | All results (paginated) |
GET |
/{prefix}/results/{id} |
Yes | Single result by ID |
Artisan Commands
lighthouse:audit
Run an audit synchronously and display a score table in the terminal:
php artisan lighthouse:audit https://example.com php artisan lighthouse:audit https://example.com --timeout=240
lighthouse:schedule
Dispatch an audit job to the queue:
php artisan lighthouse:schedule https://example.com php artisan lighthouse:schedule https://example.com --form-factor=mobile --timeout=180 php artisan lighthouse:schedule https://example.com --categories=performance --categories=seo
lighthouse:list
List recent audit results:
# Show last 10 results as a table php artisan lighthouse:list # Filter by URL php artisan lighthouse:list --url=https://example.com # Show 25 results as JSON php artisan lighthouse:list --limit=25 --format=json # Export as CSV php artisan lighthouse:list --format=csv > results.csv
Auto-pruning old records
The LighthouseAuditResult model uses MassPrunable. Add this to your scheduler (or run manually):
php artisan model:prune --model="FilamentSpatieLighthouse\Models\LighthouseAuditResult"
Records older than keep_history_for_days days (default: 30) are deleted.
Events
The plugin dispatches three events during the audit lifecycle. All events are queue-safe (no non-serializable objects).
| Event | When | Properties |
|---|---|---|
AuditStartingEvent |
Just before the audit runs | $url, $categories, $formFactor, $userId |
AuditEndedEvent |
Audit completed successfully | $url, $scores (array, 0–100 per category), $userId |
AuditFailedEvent |
Audit threw an exception | $url, $exception, $userId |
Listening to events
use FilamentSpatieLighthouse\Events\AuditEndedEvent; use FilamentSpatieLighthouse\Events\AuditFailedEvent; use FilamentSpatieLighthouse\Events\AuditStartingEvent; // In EventServiceProvider or using #[AsEventListener]: Event::listen(AuditEndedEvent::class, function (AuditEndedEvent $event) { // $event->url — the audited URL // $event->scores — ['performance' => 87, 'accessibility' => 100, ...] // $event->userId — nullable string, set when audit was triggered by a user }); Event::listen(AuditFailedEvent::class, function (AuditFailedEvent $event) { // $event->url // $event->exception // $event->userId }); Event::listen(AuditStartingEvent::class, function (AuditStartingEvent $event) { // $event->url // $event->categories — array of category strings // $event->formFactor — 'desktop' | 'mobile' // $event->userId });
Custom Result Store
Implement the ResultStore interface to plug in any storage backend (Redis, DynamoDB, etc.):
use FilamentSpatieLighthouse\ResultStores\ResultStore; use FilamentSpatieLighthouse\ResultStores\StoredAuditResults\StoredAuditResults; use Spatie\Lighthouse\LighthouseResult; class RedisResultStore implements ResultStore { public function save(string $url, LighthouseResult $result): void { // persist $result->rawResults(), $result->scores(), etc. } public function latestResults(?string $url = null): ?StoredAuditResults { // return a StoredAuditResults instance or null return new StoredAuditResults( url: $url, finishedAt: $finishedAt, rawResults: $rawResults, scores: $scores, ); } public function getHistory(?string $url = null, int $limit = 10): array { // return an array of result arrays return []; } }
Bind it in a service provider:
use FilamentSpatieLighthouse\ResultStores\ResultStore; $this->app->singleton(ResultStore::class, RedisResultStore::class);
StoredAuditResults constructor
new StoredAuditResults( url: string, finishedAt: ?Carbon, rawResults: array, // full Lighthouse JSON, keyed by audit id scores: array, // ['performance' => 87, 'accessibility' => 100, ...] rawResultPath: ?string, // filesystem path, or null if stored inline );
Extending the Page
To override any behaviour on the Lighthouse Results page, extend the base class:
namespace App\Filament\Pages; use FilamentSpatieLighthouse\Pages\LighthouseResults as BaseLighthouseResults; class LighthouseResults extends BaseLighthouseResults { // Override table columns, actions, header widgets, etc. }
Register it via the plugin:
FilamentSpatieLighthousePlugin::make() ->usingPage(\App\Filament\Pages\LighthouseResults::class)
Contributing
Contributions are welcome! Please read CONTRIBUTING.md before submitting a pull request.
Credits
- Rawand — author and maintainer
- Spatie — for spatie/lighthouse-php and spatie/laravel-package-tools
- Filament PHP — for the admin panel framework
See CREDITS.md for the full list.
Changelog
See CHANGELOG.md.
License
MIT — see LICENSE.
