spatie/browsershot
Render web pages (URLs or HTML) to images or PDFs from PHP/Laravel using headless Chrome via Puppeteer. Save screenshots/PDFs, render local HTML files, extract post-JS body HTML, and inspect requests triggered by a page.
First, ensure Node.js (v22+), npm, and Puppeteer are installed globally (npm install puppeteer --location=global). Install the package via Composer: composer require spatie/browsershot. Then, start with the most basic use case: convert a URL to an image or PDF. For example:
use Spatie\Browsershot\Browsershot;
// Save as PNG/JPEG
Browsershot::url('https://example.com')->save('screenshot.png');
// Save as PDF (detection by .pdf extension)
Browsershot::url('https://example.com')->save('report.pdf');
// Render HTML string directly
Browsershot::html('<h1>Hello World</h1>')->save('card.pdf');
This covers common first steps—snapshotting live pages, generating PDFs from HTML fragments, or local files (using htmlFromFilePath). Check vendor/spatie/browsershot/README.md and official docs at spatie.be/docs/browsershot for full method reference.
Dynamic Report Generation in Jobs: Dispatch a queued job that renders HTML templates (e.g., invoice, dashboard summary) to PDF or image. Inject Laravel view data, then use Browsershot’s html() method. Combine with Blade’s view('reports.invoice', $data)->render().
Test-Driven UI Validation: In feature tests, assert that dynamically rendered pages (e.g., dynamic pricing configurator) match expected layout by capturing and comparing images using image-diff tools (e.g., image-diff CLI).
Form Preview Generation: Allow users to preview submitted forms before saving. On form preview request, render the final HTML using html(), optionally simulate user input via type(), selectOption(), and click(), then generate image/PDF snapshot for confirmation.
Conditional Media Emulation: For responsive UI testing or print-preview, use emulateMedia('print') or emulateMediaFeatures() with prefers-color-scheme=dark. Crucial for accurate visual testing of CSS-driven themes.
Redirect & Request Auditing: When diagnosing inconsistent screenshots, inspect redirectHistory() and triggeredRequests()—especially for geo/localized redirects or A/B test endpoints that alter output.
Custom Environment for Localization: Use setEnvironmentOptions(['LANG' => 'fr-FR']) to render pages in correct locale (e.g., date formats, translated UI), even if server locale differs.
Performance Optimization: When using data URIs (e.g., SVG sprites), call disableCaptureURLs() to reduce memory footprint. Prevents Puppeteer from caching unused assets.
Font Rendering on Linux: Fonts may appear jagged on headless Linux servers (especially CentOS). Fix with addChromiumArguments(['font-render-hinting' => 'none']) or install fonts via apt install fonts-liberation.
Node Binary Discovery Failures: In isolated environments (e.g., Forge, Docker, shared hosts), explicitly set setNodeBinary() and setNpmBinary() or use setIncludePath() to extend $PATH. Verify with which node and which npm.
PDF Page Size & Margins: By default, PDFs use US Letter size. Adjust with margins() method: ->margins(10, 5, 10, 5) (top/right/bottom/left in mm). Use size('A4') or custom dimensions (->size(210, 297)).
Wait for JavaScript to Settle: Dynamic content may not be rendered synchronously. Use ->delay(500) after type()/click(), or consider ->waitForFunction('document.querySelector(".loaded")') for robustness.
POST Data Parsing: POST payload uses application/x-www-form-urlencoded. If expecting JSON, handle in middleware (client-side) or use html() instead to bypass navigation entirely.
HTML Base URL Trap: Relative assets (e.g., /img/logo.png) fail when using html() because Puppeteer lacks a base URL. Use setContentUrl('https://app.test') to ensure correct resolution.
Browser Version Mismatch: Puppeteer v23+ bundles a specific Chromium version. If chmod errors occur, explicitly install Chromium via npx puppeteer browsers install chrome. Avoid mixing global puppeteer versions.
Memory & Timeout Limits: Long pages or heavy JS may time out. Configure via ->timeout(60) (seconds) and avoid capturing full-page unnecessarily—use ->showBackground() only if needed for PDF backgrounds (adds ~2x memory).
Headless vs New Headless Mode: Use newHeadless() for modern Chrome headless (faster, more reliable) unless debugging—older headless: true may be needed for legacy Puppeteer setups. Prefer the former unless hitting --disable-gpu issues on VMs.
How can I help you explore Laravel packages today?