Using Browsershot for PDF generation

by Jeffrey van Rossum

Yesterday I was trying to tweak the invoice design for Addrow, a SaaS application I made with Laravel. I have basically two templates for the invoice, one for web display and a PDF-version.

I tried to use one template for both versions, but using Dompdf I found myself having to tweak quite a lot to get the PDF anywhere near the HTML-version so ended up with two separate versions.

As I was trying to tweak the design, I decided to look for alternatives to Dompdf and try to get to a situation where I only needed to maintain one template file. I then stumbled upon this tweet by David Hemphill.

Using Spatie's Browsershot Package

And that sort of blew my mind. David is using Spatie's Browsershot package to utilize the headless version of Chrome and generate a PDF download that way. A massive advantage is the template reusability and the PDF looks pretty darn close to the web-version. I can also use TailwindCSS to style the invoice. Pretty cool!

The package requires you to have Node and the Puppeteer library installed. But that is pretty easy to install. The package readme file, even includes instructions how to install this on a Forge provisioned server which is exactly my use case.

What does the code look like?

You can get the idea from David's screenshot, but I thought it would be nice to share some more code to give an indication of how I implemented this. I have added a pdf method on my Invoice class like this:

public function pdf()
{
    $content = view('templates.invoice', ['invoice' => $this])->render();

    return Browsershot::html($content)
        ->margins(18, 18, 24, 18)
        ->format('A4')
        ->showBackground()
        ->pdf();
}

The generated HTML is just a Blade rendered template. Then, in my InvoicesController I can do the following to stream the PDF to the browser:

public function pdf($id)
{
    $invoice = Invoice::findOrFail($id);

    $this->authorize('view', $invoice);

    return response()->stream(function () use ($invoice) {
        echo $invoice->pdf();
    }, 200, ['Content-Type' => 'application/pdf']);
}

To add this PDF as an email attachment, I use the attachData method on the Mailable class.

public function build()
{
    return $this->from(config('mail.from.address'))
        ->replyTo($this->invoice->profile->email)
        ->markdown('mail.invoice')
        ->subject(__("Your invoice is ready"))
        ->attachData($this->invoice->pdf(), __('Invoice :number', ['number' => $this->invoice->invoice_number]) . '.pdf');
}

Conclusion

In the end I am very happy with this refactor. I can now use one template and use TailwindCSS for styling. Thanks to Spatie for providing this package and David for mentioning this on Twitter. Also, thanks to Dompdf since I've used this for years and in a lot cases it might very well still be the way to go.

Comments

Talk about this article on X.
Replied by Danny Kinyua
I am browsershot but apparently am getting an error rendering the pdf? Is there anything am missing -- external CSS?
Replied by Freek Van der Herten ⚡️
Please post specific questions, with as much info as you can, in the Q&A section of the discussions on the repo.
Replied by flowdee ツ
Oh this is very interesting! I looked for something like this for long time. I don’t want to use dompdf 🙈
Replied by Schrödingers 🦘
Pointless for generating proper paginated content…@pagedjs is the way to go here
Reposted by /dev/prabakaran
Turning HTML-code or even entire pages into a PDF is incredibly simple using the Browsershot package from @spatie_be. Here is an example fro
Reposted by Markus
Turning HTML-code or even entire pages into a PDF is incredibly simple using the Browsershot package from @spatie_be. Here is an example fro
Reposted by Spatie
Turning HTML-code or even entire pages into a PDF is incredibly simple using the Browsershot package from @spatie_be. Here is an example fro
Replied by Daniel
@justusmoroni das Leben könnte so einfach sein, wenn es Fonts nicht gäbe 😂
Reposted by ArielMejiaDev
Turning HTML-code or even entire pages into a PDF is incredibly simple using the Browsershot package from @spatie_be. Here is an example fro
Reposted by /e/halil 💻🎧📌
Turning HTML-code or even entire pages into a PDF is incredibly simple using the Browsershot package from @spatie_be. Here is an example fro
Reposted by PHP Synopsis
Turning HTML-code or even entire pages into a PDF is incredibly simple using the Browsershot package from @spatie_be. Here is an example fro
Reposted by Chris Geiger 🌍🌐
Turning HTML-code or even entire pages into a PDF is incredibly simple using the Browsershot package from @spatie_be. Here is an example fro
Reposted by Ashish Nimrot
Turning HTML-code or even entire pages into a PDF is incredibly simple using the Browsershot package from @spatie_be. Here is an example fro
Reposted by Henrik Nordquist
Turning HTML-code or even entire pages into a PDF is incredibly simple using the Browsershot package from @spatie_be. Here is an example fro
Reposted by Amine TIYAL
Turning HTML-code or even entire pages into a PDF is incredibly simple using the Browsershot package from @spatie_be. Here is an example fro
Reposted by SPACETUMCO
Turning HTML-code or even entire pages into a PDF is incredibly simple using the Browsershot package from @spatie_be. Here is an example fro
Did you like this post?

If you sign up for my newsletter, I can keep you up to date on more posts like this when they are published.