Laravel Packages

Screenshot: Capture Images and PDFs with PHP and Laravel

Published
Screenshot: Capture Images and PDFs with PHP and Laravel image

Screenshot, created by Steve Bauman, captures HTML, Blade views, and web pages as PNG, JPEG, or PDF files. Its default driver launches and controls Chrome from PHP, without requiring Node.js or Puppeteer. Captures can be saved locally or stored on any configured Laravel filesystem disk.

Capture views and web pages

Pass your content to html(), view(), or url(). A learning platform could generate an image of a course completion certificate for a learner to share. This example assumes a certificates.show Blade view and a $certificate model:

use DirectoryTree\Screenshot\Facades\Screenshot;
 
Screenshot::view('certificates.show', data: ['certificate' => $certificate])
->size(width: 1440, height: 900)
->jpeg(quality: 85)
->store(path: "certificates/{$certificate->id}/preview.jpg", disk: 's3');

PNG is the default. Use jpeg() or pdf() to change the format; changing the filename extension won't do it.

JavaScript is disabled by default. For a screenshot of a public product page that uses JavaScript, enable it with javascript(). Here, $product->public_url contains the page's absolute HTTP or HTTPS URL:

use DirectoryTree\Screenshot\Facades\Screenshot;
 
Screenshot::url($product->public_url)
->javascript()
->size(width: 1280, height: 720)
->png()
->viewport()
->save(storage_path("app/product-{$product->id}-page.png"));

Screenshot waits for the page's load event. If your page fetches data or renders a chart after that event, the capture may happen before it finishes. For HTML and Blade views, use absolute asset URLs or a <base href="..."> so Chrome can load stylesheets and images.

Use size() to set the viewport Chrome uses to lay out the page. By default, images capture the full page, cropped to a maximum width of 2048 pixels and height of 10000 pixels. The viewport() call above captures only the visible viewport, within those limits. We've also covered Spatie's Laravel Screenshot for capturing web pages in Laravel.

Element captures and PDFs

Call element() with a CSS selector before selecting the output format. If the certificate page includes navigation or download buttons, wrap the certificate itself in #certificate to export just that element:

use DirectoryTree\Screenshot\Enums\PaperSize;
use DirectoryTree\Screenshot\Facades\Screenshot;
 
$source = Screenshot::view('certificates.show', ['certificate' => $certificate])
->size(1440, 900)
->element('#certificate');
 
$source->png()->store(path: "certificates/{$certificate->id}/certificate.png", disk: 's3');
 
$source->pdf()
->paper(PaperSize::A4)
->landscape()
->store(path: "certificates/{$certificate->id}/certificate.pdf", disk: 's3');

Image captures crop to the element's bounds. For PDFs, Chrome moves the element into the document body and prints it with the document's stylesheets. CSS selectors that depend on removed wrappers may need adjustment.

PDFs default to A4 portrait with 10 mm margins and printed backgrounds. Paper dimensions and margins are configurable. If your stylesheet defines an @page size, use preferCssPageSize() to give it precedence. For more on PDF rendering drivers, see our coverage of Spatie's Laravel PDF v2.

Save and reuse captures

To save a certificate preview locally and upload the same image, keep the result of capture():

use DirectoryTree\Screenshot\Facades\Screenshot;
 
$image = Screenshot::view('certificates.show', ['certificate' => $certificate])
->size(1440, 900)
->element('#certificate')
->png()
->capture();
 
$image->save(storage_path("app/certificate-{$certificate->id}.png"));
$image->store(
path: "certificates/{$certificate->id}/certificate.png",
disk: 's3',
options: ['visibility' => 'private'],
);

Calling save() or store() directly on a source starts a new capture each time. The result object holds the captured bytes, so both writes above use the same image.

The parent directory of the file passed to save() must already exist. Omit disk from store() to use Laravel's default disk. Image and PDF results also provide raw contents, Base64 data, and data URIs.

Installation

Screenshot supports Laravel 12 with PHP 8.2 or later, and Laravel 13 with PHP 8.3 or later:

composer require directorytree/screenshot
php artisan screenshot:install

The service provider registers automatically. The install command downloads Chrome for Testing if the package has no existing installation. Downloads require PHP's ZIP extension. Run php artisan screenshot:install --update to check for a newer stable version.

To use an existing Chrome or Chromium executable, set SCREENSHOT_CHROME_PATH to its path. Your server also needs Chrome's system libraries and the fonts your pages use.

For remote rendering, the package supports Gotenberg 8.38 or later. With local Chrome, named sockets let workers share a browser when they run on the same host under the same OS user and use the same private socket directory. Shared browsers also share cookies and storage.

In tests, Screenshot::fake() records captures without starting Chrome. Pair it with Storage::fake() when testing uploads: the screenshot fake still writes files.

See the Screenshot repository for configuration, driver options, and testing examples.

Yannick Lyn Fatt photo

Staff Writer at Laravel News and Full stack web developer.

Sponsored

laravelcloud logo
Laravel Cloud

Easily create and manage your servers and deploy your Laravel applications in seconds.

Visit Laravel Cloud

The latest

View all →
Reset Sticky Database Connections Between Laravel Queue Jobs image

Reset Sticky Database Connections Between Laravel Queue Jobs

Read article
PHP Security Releases Fix Two Critical RCE Vulnerabilities image

PHP Security Releases Fix Two Critical RCE Vulnerabilities

Read article
Prevent Stale Database Reads in Laravel 13.38 image

Prevent Stale Database Reads in Laravel 13.38

Read article
Critical PHP Security Release Coming Tomorrow (October 21) image

Critical PHP Security Release Coming Tomorrow (October 21)

Read article
Critical Token Guard Auth Bypass Fixed in Laravel 13.29 and 12.68 image

Critical Token Guard Auth Bypass Fixed in Laravel 13.29 and 12.68

Read article
Run Large PlanetScale Queries in Laravel With DB::olap() image

Run Large PlanetScale Queries in Laravel With DB::olap()

Read article