Easily create PDFs in Laravel apps
We’ve released a new package called spatie/laravel-pdf, a batteries-included package to generate PDFs in Laravel apps. Under the hood, it uses Chromium to generate PDFs from Blade views. You can use modern CSS features like grid, flexbox, and even frameworks like Tailwind to create beautiful PDFs.
In this post, I’d like to introduce and demonstrate the package.
Creating PDFs
Once the package has been installed, you can use the PDF
facade to create PDFs. The basis for PDF creation is HTML, and the easiest way to generate HTML in a Laravel app is by using a view.
Here's an example of creating a PDF from a Blade view.
use Spatie\LaravelPdf\Facades\Pdf;
Pdf::view('pdf.invoice')->save('/some/directory/invoice.pdf');
Under the hood, the package will, via Browsershot, spin up an instance of Chrome, load your HTML, and let Chrome save a PDF. Because Chrome has a state-of-the-art rendering engine, you should be able to use modern CSS features to create your PDF.
As a second parameter, you can pass an array of data that will be made available in the view. You might use that to pass an Eloquent model, such as an invoice, to the view.
use Spatie\LaravelPdf\Facades\Pdf;
Pdf::view('pdf.invoice', ['invoice' => $invoice])
->save('/some/directory/invoice.pdf');
In addition to saving locally, you could save your PDF on any of your configured disks.
Here's an example of saving a PDF to the s3
disk.
use Spatie\LaravelPdf\Facades\Pdf;
Pdf::view('invoice')
->disk('s3')
->save('invoice-april-2022.pdf');
Instead of using a Blade view, you can also create a PDF from a string of HTML.
use Spatie\LaravelPdf\Facades\Pdf;
Pdf::html('<h1>Hello world!!</h1>')->save('/some/directory/invoice.pdf');
Responding with PDFs
In a controller, you can create and return a PDF using the pdf()
function.
use function Spatie\LaravelPdf\Support\pdf;
class DownloadInvoiceController
{
public function __invoke(Invoice $invoice)
{
return pdf()
->view('pdf.invoice', compact('invoice'))
->name('invoice-2023-04-10.pdf');
}
}
By default, the PDF will be inlined in the browser. This means that the PDF will be displayed in the browser if the browser supports it. If the user tries to download the PDF, it will be named "invoice-2023-04-10.pdf". We recommend that you always name your PDFs.
You can use the download()
method to force the PDF to be downloaded.
use function Spatie\LaravelPdf\Support\pdf;
class DownloadInvoiceController
{
public function __invoke(Invoice $invoice)
{
return pdf()
->view('pdf.invoice', compact('invoice'))
->name('invoice-2023-04-10.pdf')
->download();
}
}
Using JavaScript
The JavaScript in your HTML will be executed by Chrome when the PDF is created. You could use this to have a JavaScript charting library render a chart.
Here's a simple example. If you have this Blade view...
<div id="target"></div>
<script>
document.getElementById('target').innerHTML = 'hello';
</script>
... and render it with this code...
use Spatie\LaravelPdf\Facades\Pdf;
Pdf::view('your-view')->save($pathToPdf);
... the text hello
will be visible in the PDF.
Testing PDFs
Generating a PDF can be slow, as an entire instance of Chrome needs to be started. In your tests, this slowness can be bothersome. That’s why the package ships with a PDF fake. No PDF generation will occur when using this fake, and your tests will run faster.
// in your test
use Spatie\LaravelPdf\Facades\Pdf;
beforeEach(function () {
Pdf::fake();
});
When PDF generation is faked, you can use some powerful assertion methods.
You can use the assertSaved
method to assert that a PDF was saved with specific properties. You should pass it a callable which will receive an instance of Spatie\LaravelPdf\PdfBuilder
. If the callable returns true
, the assertion will pass.
use Spatie\LaravelPdf\Facades\Pdf;
use Spatie\LaravelPdf\PdfBuilder;
Pdf::assertSaved(function (PdfBuilder $pdf) {
return $pdf->downloadName === 'invoice.pdf'
&& str_contains($pdf->html, 'Your total for April is $10.00'));
});
The assertRespondedWithPdf
method can be used to assert that a PDF was generated and returned as a response.
Imagine you have this route:
use Spatie\LaravelPdf\Facades\Pdf;
Route::get('download-invoice', function () {
return pdf('pdf.invoice')->download('invoice-for-april-2022.pdf');
});
In your test for this route you can use the assertRespondedWithPdf
to ensure that a PDF was generated and returned as a download. You can even make assertions on the content of the PDF.
use Spatie\LaravelPdf\Facades\Pdf;
use Spatie\LaravelPdf\PdfBuilder;
it('can download an invoice', function () {
$this
->get('download-invoice')
->assertOk();
Pdf::assertRespondedWithPdf(function (PdfBuilder $pdf) {
return $pdf->downloadName === 'invoice-for-april-2022.pdf'
&& $pdf->isDownload()
&& str_contains($pdf->html, 'Your total for April is $10.00'));
});
});
Generating PDFs on AWS Lambda
Generating PDFs locally can be resource-intensive. If you're having to generate a lot of PDFs or having trouble installing the necessary dependencies on your server, you may want to consider using AWS Lambda to generate your PDFs.
To generate PDFs on AWS Lambda, you must install these two packages in your app.
- hammerstone/sidecar: this allows you to execute AWS Lambda functions from your Laravel application
- wnx/sidecar-browsershot: this package contains a version of Browsershot that can run on AWS Lambda via Sidecar
With these two packages installed, you can generate PDFs on AWS Lambda like this:
Pdf::view('pdf.invoice', $data)
->generateOnLambda()
->save('invoice.pdf');
If you want to create all PDFs in your app on Lambda, you can set it as a default like this:
// typically, in a service provider
Pdf::default()->generateOnLambda();
In closing
I had fun coding this package, and I hope you will enjoy using it. There are many options that I didn’t cover in this post, such as formatting PDFS, adding headers and footers, using page breaks, and much more!
Laravel PDF uses headless Chrome to generate PDFs. This is a great solution, as you can use any CSS you want, and it will be rendered correctly. However, generating a PDF this way can be resource-intensive. If you don’t like this trade-off, a great alternative to check out is laravel-dompdf.
Laravel PDF is just one of many packages that we've made. You'll find an extensive list of Laravel and PHP packages we released previously on our company website. There's probably something there for your next project. If you like our open-source stuff, be sure to also look at our paid products that make working on open-source sustainable for our company.
What are your thoughts on "Easily create PDFs in Laravel apps"?