dompdf Alternatives: HTML to PDF in Laravel Without a Headless Browser

dompdf Alternatives: HTML to PDF in Laravel Without a Headless Browser

Generating a PDF in Laravel means choosing which problem you want to own. dompdf makes you write CSS like it's 2011. Snappy chains you to an abandoned binary. Browsershot renders beautifully and quietly turns your PHP server into a Node and Chrome host.

There is a fourth option: render your Blade view exactly as you would for the browser, POST the HTML to an API and get back a real vector PDF. This article covers why the three usual suspects hurt, and what the API route looks like in practice.

Where dompdf falls over

dompdf is a pure PHP renderer, which is exactly why people reach for it: composer require and you are generating PDFs with no binaries and no system dependencies. The cost is the rendering engine. dompdf implements CSS 2.1 with a partial layer of CSS3, and that ceiling shapes everything you build on it.

No flexbox. No grid. Floats behave, mostly, which is why every dompdf invoice template on the internet is built from floats and tables. Web fonts need registering with the font loader rather than just working. And because there is no JavaScript engine, anything your view computes client-side simply never happens.

The practical consequence is a fork in your styles. You have the stylesheet your app actually uses, and a second, degraded one for PDFs, and they drift apart with every design change. The invoice your customer sees on the web and the one attached to their email are two implementations of the same design.

Snappy wraps a dead engine

laravel-snappy hands the work to wkhtmltopdf, which renders with a real browser engine. The catch is which one: a Qt WebKit build frozen around 2012, from a project that is now archived and unmaintained.

That engine predates CSS grid entirely and speaks only the ancient 2009 flexbox syntax. Custom properties do not exist. Security patches stopped when the project did. We have covered the wkhtmltopdf problem in detail before, but for Laravel specifically the summary is: you are compiling a binary onto your server so that a browser from over a decade ago can misinterpret your modern CSS.

Browsershot renders correctly, on your infrastructure

Browsershot fixes the rendering problem properly. It drives headless Chrome through Puppeteer, so flexbox, grid, custom properties and web fonts all work exactly as they do in your browser.

The trade is operational. Your Laravel server now needs Node, Puppeteer and a Chrome binary, kept in version agreement with each other, patched, and given enough memory for Chrome's appetite. That is fine on a box you control. It is a headache on shared hosting, a fight on serverless and a genuine ops burden the moment PDF volume grows. We wrote up the same problem for image rendering in our Browsershot piece; PDFs inherit all of it.

Render the Blade view, POST it, get a PDF

The HTML to PDF API route keeps Chrome rendering and removes Chrome from your server. Render your view to a string, send it with format: "pdf", get back a URL to the finished document:

use Illuminate\Support\Facades\Http;

$response = Http::withHeaders([
    'X-API-Key' => config('services.html2img.key'),
])->post('https://app.html2img.com/api/html', [
    'html' => view('invoices.show', ['invoice' => $invoice])->render(),
    'format' => 'pdf',
]);

$url = $response->json('url');
// https://i.html2img.com/image-....pdf

The response is the same JSON envelope as an image render, the file is a real vector PDF, and it costs one credit like any other render. Text stays selectable and searchable, web fonts are embedded, and long content paginates across A4 pages on its own. Because the render happens in current Chrome, the stylesheet is the one your app already uses. No fork.

Or use the Laravel package

From version 1.1, the Laravel package exposes the same option as an enum:

composer require html2img/html2img-laravel
use Html2img\Enum\Format;
use Html2img\Laravel\Facades\Html2img;
use Html2img\Request\HtmlRequest;

$response = Html2img::html(new HtmlRequest(
    html: view('invoices.show', ['invoice' => $invoice])->render(),
    format: Format::Pdf,
));

$path = Html2img::store($response, "invoices/{$invoice->number}.pdf");

Html2img::store() drops the finished PDF onto any filesystem disk your app has configured, S3 included, which is usually where an invoice needs to end up anyway.

Attach it to a Mailable

The most common destination for a Laravel PDF is an email. With the file stored on a disk, attachment is one line in the Mailable:

use Illuminate\Mail\Mailables\Attachment;

public function attachments(): array
{
    return [
        Attachment::fromStorage("invoices/{$this->invoice->number}.pdf")
            ->withMime('application/pdf'),
    ];
}

If you would rather show the invoice inline in the email body instead of attaching a document, render it as a PNG with the same markup and skip the format parameter. Same view, two outputs.

Know what the PDF is

Worth being clear-eyed about the output so your markup plays to it. Pages are A4 portrait and content reflows to the page width, so width, height, dpi, fullpage and selector do nothing in PDF mode. The document renders with your normal screen CSS rather than @media print, which is the point: the PDF looks exactly like the image render would. Write the view to flow like a document rather than to fixed pixel dimensions and pagination takes care of itself. The format parameter docs cover the full behaviour.

For big documents, add a webhook_url and we will POST you the finished URL rather than holding your request open, which pairs naturally with a queued job.


Need invoices, certificates or reports rendered from the Blade views you already have, without a browser on your server? Browse the templates gallery or read the docs to get started.

Mike Griffiths

Mike has spent the last 20 years crafting software solutions for all kinds of amazing businesses. He specializes in building digital products and APIs that make a real difference. As an expert in Laravel and a voting member on the PHP language, Mike helps shape the future of web development.