Reader Stacks

Converting HTML to PDF in Laravel

DomPDF renders using its own internal engine, not a real browser — CSS Grid, Flexbox, and modern layout features are only partially supported, which is exactly why a complex layout can look noticeably different in the generated PDF.

Converting HTML to PDF in Laravel

Generating a PDF from an HTML/Blade view in Laravel — an invoice, a report, a certificate — is most commonly handled through barryvdh/laravel-dompdf, a wrapper around the DomPDF library, with one important limitation worth knowing upfront.

Installing the package

composer require barryvdh/laravel-dompdf

Generating a PDF from a Blade view

use Barryvdh\DomPDF\Facade\Pdf;

public function downloadInvoice(Order $order)
{
    $pdf = Pdf::loadView('invoices.pdf', compact('order'));

    return $pdf->download('invoice-'.$order->id.'.pdf');
}

The Blade view, styled with plain inline or embedded CSS




    


    

Invoice #{{ $order->id }}

@foreach ($order->items as $item) @endforeach
{{ $item->name }} {{ $item->price }}

Displaying the PDF in the browser instead of downloading it

return $pdf->stream('invoice.pdf');

stream() sends the PDF with headers that let the browser display it inline (if the browser supports inline PDF viewing) — download() instead forces a save-to-disk download prompt; the choice between them depends purely on the desired user experience for viewing versus saving the document.

Saving the generated PDF to disk instead of sending it to the browser

$pdf = Pdf::loadView('invoices.pdf', compact('order'));
$pdf->save(storage_path('app/invoices/invoice-'.$order->id.'.pdf'));

Setting paper size and orientation

$pdf = Pdf::loadView('reports.summary', compact('data'))
    ->setPaper('a4', 'landscape');

The critical limitation: DomPDF is not a real browser

DomPDF renders HTML/CSS using its own internal layout engine, not an actual browser rendering engine like Chromium — modern CSS features (Flexbox, CSS Grid, many custom properties) are only partially supported or not supported at all, which is exactly why a layout that looks correct in a real browser can appear noticeably broken or different once rendered as a PDF; simple table-based or basic block/inline layouts are the safest, most reliable approach for DomPDF specifically.

A more accurate alternative for complex layouts: a headless browser

// Using a package like spatie/browsershot, which drives headless Chrome
Browsershot::html($html)->savePdf(storage_path('app/invoice.pdf'));

For a genuinely complex, modern CSS-heavy layout that needs to render pixel-accurately, a headless-browser-based tool (like spatie/browsershot, which actually drives Chrome under the hood) produces meaningfully more accurate results than DomPDF — at the cost of a heavier server dependency (an actual Chrome/Chromium binary) and generally slower generation time per PDF.

Embedding an image in the PDF

Using an absolute server filesystem path (via public_path()) rather than a normal web URL is necessary for DomPDF to reliably embed a local image — it doesn't always resolve a relative or web-based URL path the same way a real browser would when rendering the HTML into a PDF.