Self-hosted service
spatie/laravel-pdf avatar
spatie/laravel-pdf

laravel-pdf, one PDF API in Laravel with six rendering engines underneath

Create PDF files in Laravel apps

1,050 stars106 forksPHPMIT

At a glance

What is it?
laravel-pdf is a Spatie package that renders a Blade view into a PDF through a facade, and lets you swap the engine behind that call. The supported drivers are Browsershot and chrome-php with Chromium, Gotenberg in Docker, Cloudflare Browser Run, WeasyPrint in Python, and DOMPDF in pure PHP, which is a wide enough range that most teams can pick one that matches what they are already able to run.
Who is it for?
Adopt laravel-pdf if you need to produce PDFs from a Laravel application and would rather not commit to one rendering engine, since the driver choice is a configuration decision rather than an architectural one. Skip it if you already have a rendering service and a stable template pipeline, because the value here is the Laravel integration rather than the PDF generation.
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 18 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 October 21, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Six drivers, and why the choice matters

The package provides a simple way to create PDFs in Laravel apps, and the interesting part is that it supports multiple drivers. Browsershot runs Chromium, chrome-php/chrome also runs Chromium, Gotenberg is Docker-based, Cloudflare Browser Run is a hosted option, WeasyPrint is Python-based, and DOMPDF is pure PHP. The guidance in the documentation is a good summary of how to choose. You can use modern CSS features like grid and flexbox with the Chromium-based drivers, you can use WeasyPrint for excellent CSS Paged Media support, or you can choose DOMPDF for a zero-dependency setup. That is the real decision: your PDF looks like what a browser renders, or like what a print engine renders, or like what a pure PHP renderer manages, and those are three visibly different outcomes for the same template. The abstraction lets you start on the easiest driver and move later.

code
Browsershot
Gotenberg
Cloudflare Browser Run
WeasyPrint
DOMPDF
chrome-php/chrome

The facade, and the two ways to use the result

The API is small. You call a view method on the Pdf facade with a template name and a data array, chain a format call, and then either save the file or return it. The first example renders the Blade view pdfs.invoice with the given data and saves it as a PDF file. The second shows the response path, where a controller returns the PDF as a response, using a name call so the download has a filename. That the whole surface is a facade with a handful of chainable methods is what makes the driver swap invisible to application code, and it is why a team can try WeasyPrint in a branch without touching a controller. The formatting call is where the page size lives, and the example uses a4.

php
use Spatie\LaravelPdf\Facades\Pdf;

Pdf::view('pdfs.invoice', ['invoice' => $invoice])
    ->format('a4')
    ->save('invoice.pdf')

Testing the PDF, not just the response

The package lets you test your PDFs, and the mechanics are the part worth borrowing even if you use a different library. You call Pdf::fake() so no real rendering happens, create the invoice with a factory, make the request against the route, and assert the response is ok. Then you use Pdf::assertRespondedWithPdf with a callback receiving the PdfBuilder, and inside the callback you check that the PDF contains the text you expect. The assertion helper takes a closure, so you can make any statement about the content, and the check here is that the document contains a particular string. That is a materially stronger test than asserting a 200 response, because a PDF endpoint that returns an empty document with a valid content type passes the weak test and fails this one.

php
Pdf::fake();

$invoice = Invoice::factory()->create();

$this->get(route('download-invoice', $invoice))
    ->assertOk();

Pdf::assertRespondedWithPdf(function (PdfBuilder $pdf) {
    return $pdf->contains('test');
});

What the test suite needs, and the two ecosystems involved

Running the testsuite needs Puppeteer installed, and the documentation points at the Browsershot requirements, noting that a global npm install of puppeteer will usually do it. You also need the pdftotext command line tool, which is part of the poppler-utils package, and the note says brew install poppler-utils will usually suffice. The reason a PHP package needs a PDF text extractor is that asserting on a PDF means reading its text back out, and pdftotext is how the assertions are checked. The JavaScript side of the repository is correspondingly small, with three dependencies: pixelmatch and pngjs for image comparison and puppeteer for the browser. The Python side is a single requirement, weasyprint at 64 or newer, because the WeasyPrint driver runs in a separate process. So the package pulls in a browser, a PDF tool and optionally a Python runtime, and the environment file shows the third: empty CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID entries for the Cloudflare Browser Run driver.

json
"dependencies": {
    "pixelmatch": "^5.3.0",
    "pngjs": "^7.0.0",
    "puppeteer": "^22.8.2"
}

The Laravel Boost skill, and the AI agent angle

The package ships with a Laravel Boost skill, and after installing the package you run php artisan boost:install to register it, with the stated purpose of helping AI agents in your project generate correct PDF code. That is a small feature with a wider implication. A PDF API has a lot of surface for a code generator to get wrong, since the driver decides which CSS features are legal and the builder methods have platform-specific behaviour, so a skill that carries those rules into the agent's context is a real saving. The repository also carries an ide.json file, which is how the package advertises its helpers to an IDE, and the documentation lives on the Spatie documentation site rather than in the repository, while the repository keeps a docs/ directory and a workbench/ directory for the local package development harness.

code
php artisan boost:install

The repository shape, and the release cadence

The tree is that of a mature Spatie package. There is src/ with the implementation, config/ for the package configuration, resources/ for the views and translations, tests/ and a testbench.yaml for the package testing harness, a workbench/ directory, and the usual configuration files including phpstan.neon.dist with a baseline, phpunit.xml.dist, and the UPGRADING.md file that tells you what breaks between major versions. The changelog is a separate file that the documentation points to for what has changed recently. The release history is steady: 2.14.0 on 2026-10-05, 2.13.1 on 2026-09-01 and 2.13.0 on 2026-08-17, and the last push to the repository was on 2026-10-05. The licence is MIT with the file at the root, and the project credits Freek Van der Herten and the contributors list. It is a package in the sense that most Laravel users mean by that: a well-scoped integration with an upstream tool, tested, documented elsewhere, and released often.

code
src/
config/
resources/
tests/
workbench/
testbench.yaml

Where it fits, and what to check first

The boundary of this package is a decision worth making explicitly. It does not generate PDF documents from a document model, so if you want an invoice system where the layout is defined once and reused across PDF, email and print, this is not the tool. It renders a Blade view, which means your PDF looks like a web page, and the driver decides how faithfully. The second thing to decide is which engine you will run in production, because the driver affects your deployment, not just your build. The third is the test story, and the one to borrow regardless of driver, because the fake and the closure-based assertion turn PDF generation into something a test suite can check. And the practical check is the rendering environment itself, since a Chromium driver needs a browser in the container and a WeasyPrint driver needs Python and its native libraries, which is where a lot of production surprises with PDF generation come from.

code
You can use modern CSS features like grid and flexbox with the
Chromium-based drivers, use WeasyPrint for excellent CSS Paged Media
support, or choose DOMPDF for a zero-dependency setup.

Editorial conclusion

Adopt laravel-pdf if you need to produce PDFs from a Laravel application and would rather not commit to one rendering engine, since the driver choice is a configuration decision rather than an architectural one. Skip it if you already have a rendering service and a stable template pipeline, because the value here is the Laravel integration rather than the PDF generation. Verify three things before you pick a driver: that the engine you choose can actually run in your deployment, since Chromium needs a browser, WeasyPrint needs Python, Gotenberg needs Docker and DOMPDF needs nothing but PHP; that the CSS features you plan to use are supported, because modern layout features such as grid and flexbox only work on the Chromium-based drivers while WeasyPrint is the one for CSS Paged Media; and that you write a test, because the package ships a fake and an assertion helper that make it practical to check the generated document rather than only that a response came back.

Frequently asked questions

Which PDF drivers does the spatie/laravel-pdf package support?

It supports multiple drivers: Browsershot with Chromium, chrome-php/chrome with Chromium, Gotenberg which is Docker-based, Cloudflare Browser Run, WeasyPrint which is Python-based, and DOMPDF which is pure PHP.

How do I render a PDF from a Blade view in Laravel?

Use the Pdf facade, call the view method with the template name and the data, chain a format call such as a4, and then either save the file or return it from a controller with a name call to set the download filename.

How can I test PDF generation in a Laravel application?

Call Pdf::fake() so nothing is really rendered, make the request against your route, assert the response is ok, and then use Pdf::assertRespondedWithPdf with a callback that receives the PdfBuilder and checks the document content, for example that it contains a particular string.

What do I need to run the laravel-pdf test suite?

You need Puppeteer installed for the Browsershot requirements, and the pdftotext command line tool from the poppler-utils package. The repository's JavaScript dependencies are pixelmatch, pngjs and puppeteer, and the Python requirement is weasyprint 64 or newer.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. spatie/laravel-pdf 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-laravel-pdf.svg)](https://hysenlabs.com/projects/spatie-laravel-pdf)

Was this useful?