Open-source project
spatie/browsershot avatar
spatie/browsershot

spatie/browsershot: rendering HTML to images and PDFs through Puppeteer

Convert HTML to an image, PDF or string

5,247 stars508 forksPHPMIT

At a glance

What is it?
Browsershot is a PHP wrapper that drives headless Chrome via Puppeteer to turn URLs or HTML strings into images, PDFs or post-JavaScript body HTML. It is a good fit when your output must match a real browser, and the wrong tool when you cannot run Node on the host.
Who is it for?
Adopt Browsershot when your PDF or screenshot has to match what Chrome actually paints, including JavaScript-driven layout, and you can install Node and Puppeteer on the machine that renders. Do not adopt it when Node cannot be installed, when a pure-PHP renderer is enough, or when you need a long-lived rendering service shared across languages.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 83 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What Browsershot solves, and for whom

Server-side PDF generation in PHP has traditionally meant a layout engine that reimplements a subset of CSS. Browsershot takes the opposite route: it hands the page to a real Chrome build and lets the browser do the layout. The README states the package "can convert a web page to an image or PDF" and that the conversion "is done behind the scenes by Puppeteer which runs a headless version of Google Chrome". That single decision explains both its strengths and its operational cost. Anything Chrome renders, Browsershot renders: web fonts, flexbox, grid, CSS transforms, canvas, and content that only exists after JavaScript runs. The audience is PHP and Laravel developers who already produce invoices, tickets, reports or previews server-side and who have been fighting a CSS-in-PHP renderer. It is less interesting to teams whose PDFs are simple text with a header, because those teams pay a Chrome download and a Node dependency for output a lighter library could produce.

The Puppeteer subprocess behind every call

Browsershot is a PHP facade over a Node process. The PHP side builds a description of the job, and a bundled Node script drives Puppeteer to open a page, navigate or set content, wait for whatever conditions you configured, and then capture. The README shows the shape of the API: a static constructor such as Browsershot::url or Browsershot::html, chained configuration methods, and a terminal method that produces output. The terminal methods are the interesting part, because they return different types. save writes a file to disk, bodyHtml returns the page body after JavaScript has executed, and triggeredRequests returns an array of the requests the page made, with each entry exposing a url key. That last one is a debugging tool as much as a feature: if a stylesheet or font is missing from your PDF, the request list tells you whether Chrome ever asked for it. Waiting is configured through chained methods rather than a fixed timeout, and the README mentions newHeadless for Chrome's newer headless mode. The architecture has a consequence worth stating plainly: the PHP process is a supervisor, not the renderer. Memory pressure, crashes and hangs happen in the Node and Chrome processes, and the quality of your error messages depends on how that subprocess output is surfaced.

Installing Browsershot and rendering your first PDF

The PHP package comes from Packagist through Composer. The README does not print the require line, but it points at the documentation site for requirements, and the testing section states that Puppeteer must be installed, suggesting a global install as the usual route.

bash
composer require spatie/browsershot
npm -g i puppeteer

With both in place, rendering a URL to a PDF is one chain. The README gives this example, and the format is chosen by the file extension passed to save: a pdf extension produces a PDF.

php
use Spatie\Browsershot\Browsershot;

Browsershot::url('https://example.com')->save('example.pdf');

If you are rendering a template you already built in PHP, swap the constructor. The README shows both an inline string and a path to an existing file.

php
Browsershot::html('<h1>Hello world!!</h1>')->save('example.pdf');
Browsershot::htmlFromFilePath('/local/path/to/file.html')->save('example.pdf');

What you should see is a file at the path you passed. If the file is empty or the process exits non-zero, the Node or Chrome side failed before capture, and that is where to look first.

Reading a page after JavaScript instead of capturing it

Not every job wants a file. Browsershot can return the DOM after scripts have run, which makes it usable as a scraping or verification step rather than only a renderer. The README's example calls bodyHtml on a URL and comments that it "returns the html of the body". That is a different failure profile from a PDF: you get a string back, and if the page never finished loading, you get an incomplete string rather than an obviously broken file. Pairing it with the request list is the practical way to check your assumptions.

php
$requests = Browsershot::url('https://example.com')
    ->triggeredRequests();

foreach ($requests as $request) {
    $url = $request['url'];
}

The README labels that URL as https://example.com/ in a comment. Treat triggeredRequests as an inspection aid for debugging a render that came out wrong, not as a crawler: it reports what one page load asked for, nothing more.

Where Browsershot is the wrong tool

The hard constraint is Node and Puppeteer on the rendering host. The README's own alternatives section is candid about this: if you cannot install Node and Puppeteer, it points at v2 of the package, which used the Chrome headless CLI, and notes that v2 "is not maintained anymore, but should work pretty well". It also mentions v1, built on the abandoned PhantomJS binary. So the escape hatches exist but are explicitly unmaintained, which means a team that cannot install Node is choosing between an old branch and a different library. The second limitation is operational weight. A Chrome process per render is heavier than a pure-PHP renderer, and it needs a writable, sandboxed environment; container images typically need extra care around Chrome's sandbox flags. The README does not document those flags, so plan on reading the documentation site's requirements page rather than the README. Third, Browsershot is not a rendering service. There is no queue, no worker pool and no HTTP API in what the README describes; concurrency is your problem, and a burst of simultaneous renders will spawn a burst of Chrome processes.

How it differs from dompdf and Gotenberg

The two comparisons people search for map onto two genuinely different strategies. dompdf is a PHP library that parses HTML and CSS and draws the PDF itself. Nothing leaves the PHP process, there is no Node dependency, and deployment is a Composer install. The trade is fidelity: a renderer that reimplements CSS will not match Chrome on modern layout features, web fonts or anything that requires script execution, and Browsershot's whole reason to exist is that gap. Gotenberg sits on the other side. It is a separate service you call over HTTP that performs document conversion, so the rendering engine runs outside your PHP application. That suits polyglot stacks where PHP is one of several callers, and it moves the Chrome dependency from every application host to one container. The cost is a network hop and a service to run and monitor. Browsershot keeps the call inside PHP, which is simpler for a single Laravel app and more awkward when several services need the same rendering.

Versioning, maintenance and the MIT licence

The repository is not archived, and the last push was on 2026-07-09, which is recent enough that the project is still being worked on. Recent releases include 5.4.0 on 2026-05-26, 5.3.0 on 2026-04-27 and 5.2.3 on 2026-02-18, so the 5.x line is where current work happens. That matters for upgrade cost: the README's own alternatives section tells you v1 and v2 are different architectures (PhantomJS and the Chrome CLI respectively), and v2 is stated as unmaintained, so a project still on those branches is not receiving fixes. Staying on 5.x means tracking Puppeteer and Chrome releases too, since the package delegates rendering to them; a Chrome update can change rendering output even when the PHP dependency has not moved. The licence is MIT, which permits commercial and closed-source use and requires preserving the copyright notice and licence text. That is a summary of the licence identifier given in the repository, not legal advice; read LICENSE.md for the actual terms.

Editorial conclusion

Adopt Browsershot when your PDF or screenshot has to match what Chrome actually paints, including JavaScript-driven layout, and you can install Node and Puppeteer on the machine that renders. Do not adopt it when Node cannot be installed, when a pure-PHP renderer is enough, or when you need a long-lived rendering service shared across languages. Before committing, verify that Puppeteer launches on your target host under the same user as PHP, that Chrome's sandbox settings work in your container, and that your chosen output path extension matches the format you expect, since the save method decides between image and PDF from the extension.

Frequently asked questions

What is the difference between dompdf and spatie/browsershot?

dompdf renders HTML and CSS inside PHP, while Browsershot delegates the render to headless Chrome through Puppeteer. Browsershot therefore needs Node and Puppeteer installed, and in exchange it produces output that matches a real browser, including pages that depend on JavaScript.

How is spatie/browsershot different from Gotenberg?

Gotenberg is a separate service reached over HTTP, which keeps the rendering engine off your application hosts. Browsershot runs the Chrome render from inside your PHP process, so there is no extra service to run, but Node and Puppeteer must be present wherever PHP runs.

Does spatie/browsershot need Node and Puppeteer on the server?

Yes. The README states the conversion is done by Puppeteer running a headless version of Google Chrome, and its testing section says Puppeteer must be installed, usually with a global npm install.

How does spatie/browsershot decide between saving an image and saving a PDF?

It looks at the extension of the path passed to the save method. The README notes that a PDF is saved when that path has a pdf extension.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. spatie/browsershot on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/spatie-browsershot.svg)](https://hysenlabs.com/projects/spatie-browsershot)