This article compares popular PDF generation solutions in Laravel.
📋 Table of Contents
Introduction
This article demonstrates how to implement two popular solutions for PDF generation in Laravel: Spatie Laravel PDF (with Browsershot) and barryvdh/laravel-dompdf. Both have their strengths and limitations—below you'll find setup instructions, usage examples, a comparison, and recommendations for when to use each.
Installation & Setup
1. Spatie Laravel PDF (Browsershot)
- Install the package:
composer require spatie/laravel-pdf npm install puppeteer npx puppeteer browsers install chrome
- Make sure Chromium is installed in your Docker PHP image (if using Docker):
RUN apt-get update \ && apt-get install -y wget gnupg2 \ && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \ && echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list \ && apt-get update \ && apt-get install -y google-chrome-stable \ && apt-get clean \ && rm -rf /var/lib/apt/lists/*
2. barryvdh/laravel-dompdf
- Install the package:
composer require barryvdh/laravel-dompdf php artisan vendor:publish --provider="Barryvdh\DomPDF\ServiceProvider"
- No extra system dependencies required. Styling is limited to basic CSS.
Example Routes and Controller
In routes/web.php
:
Route::get('/pdf/spatie', [PdfDemoController::class, 'spatie'])->name('pdf.spatie');
Route::get('/pdf/dompdf', [PdfDemoController::class, 'dompdf'])->name('pdf.dompdf');
In app/Http/Controllers/PdfDemoController.php
:
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Barryvdh\DomPDF\Facade\Pdf as DomPdf;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Spatie\Browsershot\Browsershot;
use Spatie\LaravelPdf\Facades\Pdf;
class PdfDemoController extends Controller
{
public function spatie(Request $request)
{
$data = [
'user' => [
'name' => 'John Doe',
'email' => '[email protected]',
],
'items' => [
['name' => 'Super Product', 'qty' => 2, 'price' => 199.99],
['name' => 'Mega Service', 'qty' => 1, 'price' => 499.00],
],
'total' => 199.99 * 2 + 499.00,
];
return Pdf::view('pdfs.spatie-invoice', $data)
->withBrowsershot(function (Browsershot $browsershot): void {
$browsershot->noSandbox()->format('A4');
})
->download('spatie-invoice.pdf');
}
public function dompdf(Request $request): Response
{
$data = [
'invoice_number' => 'FV/2024/05/001',
'date' => now()->toDateString(),
'client' => 'Jane Doe',
'items' => [
['name' => 'Product A', 'qty' => 3, 'price' => 100],
['name' => 'Product B', 'qty' => 1, 'price' => 250],
],
'total' => 3 * 100 + 250,
];
return DomPdf::loadView('pdfs.dompdf-invoice', $data)->download('dompdf-invoice.pdf');
}
}
Example Views
Spatie Laravel PDF (Tailwind, modern layout)
resources/views/pdfs/layouts/pdf.blade.php
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>@yield('title', 'PDF')</title>
<style>
body { font-family: 'Inter', sans-serif; }
</style>
@yield('styles')
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100 p-8">
@yield('content')
</body>
</html>
resources/views/pdfs/spatie-invoice.blade.php
:
@extends('pdfs.layouts.pdf')
@section('title', 'Invoice (Spatie Laravel PDF)')
@section('content')
<div class="max-w-2xl mx-auto bg-white rounded shadow p-8">
<div class="flex justify-between items-center mb-8">
<div>
<h1 class="text-2xl font-bold text-gray-800">INVOICE</h1>
<p class="text-gray-500">Seller: <span class="font-semibold">{{ $user['name'] }}</span></p>
<p class="text-gray-500">Email: {{ $user['email'] }}</p>
</div>
<div class="text-right">
<p class="text-gray-500">Date: {{ now()->toDateString() }}</p>
<p class="text-gray-500">Invoice No: INV/{{ now()->format('Y') }}/{{ now()->format('m') }}/001</p>
</div>
</div>
<table class="w-full mb-8 text-sm border">
<thead>
<tr class="bg-gray-200">
<th class="p-2 border">#</th>
<th class="p-2 border text-left">Item</th>
<th class="p-2 border">Quantity</th>
<th class="p-2 border">Unit Price</th>
<th class="p-2 border">Total</th>
</tr>
</thead>
<tbody>
@foreach($items as $i => $item)
<tr class="border-b">
<td class="p-2 border text-center">{{ $i+1 }}</td>
<td class="p-2 border">{{ $item['name'] }}</td>
<td class="p-2 border text-center">{{ $item['qty'] }}</td>
<td class="p-2 border text-right">${{ number_format($item['price'], 2) }}</td>
<td class="p-2 border text-right">${{ number_format($item['qty'] * $item['price'], 2) }}</td>
</tr>
@endforeach
</tbody>
<tfoot>
<tr>
<td colspan="4" class="p-2 border text-right font-bold">Total</td>
<td class="p-2 border text-right font-bold">${{ number_format($total, 2) }}</td>
</tr>
</tfoot>
</table>
<div class="text-xs text-gray-400 mt-8">Generated automatically by Spatie Laravel PDF + TailwindCSS</div>
</div>
@endsection
DomPDF (simple layout)
resources/views/pdfs/dompdf-invoice.blade.php
:
@extends('pdfs.layouts.pdf')
@section('title', 'Invoice (Spatie Laravel PDF)')
@section('styles')
<style>
body { font-family: DejaVu Sans, sans-serif; }
table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }
th, td { border: 1px solid #ccc; padding: 6px 10px; }
th { background: #eee; }
.text-right { text-align: right; }
.text-center { text-align: center; }
</style>
@endsection
@section('content')
<body>
<h2>INVOICE (DomPDF)</h2>
<p>Invoice No: <strong>{{ $invoice_number }}</strong></p>
<p>Date: <strong>{{ $date }}</strong></p>
<p>Client: <strong>{{ $client }}</strong></p>
<table>
<thead>
<tr>
<th>#</th>
<th>Item</th>
<th>Quantity</th>
<th>Unit Price</th>
<th>Total</th>
</tr>
</thead>
<tbody>
@foreach($items as $i => $item)
<tr>
<td class="text-center">{{ $i+1 }}</td>
<td>{{ $item['name'] }}</td>
<td class="text-center">{{ $item['qty'] }}</td>
<td class="text-right">${{ number_format($item['price'], 2) }}</td>
<td class="text-right">${{ number_format($item['qty'] * $item['price'], 2) }}</td>
</tr>
@endforeach
</tbody>
<tfoot>
<tr>
<td colspan="4" class="text-right"><strong>Total</strong></td>
<td class="text-right"><strong>${{ number_format($total, 2) }}</strong></td>
</tr>
</tfoot>
</table>
<p style="font-size: 10px; color: #888;">Generated automatically by barryvdh/laravel-dompdf</p>
</body>
@endsection
Comparison & Recommendations
Feature | Spatie Laravel PDF (Browsershot) | barryvdh/laravel-dompdf |
---|---|---|
Engine | Headless Chrome (Puppeteer) | DomPDF (PHP) |
Requires Chrome/Chromium | Yes | No |
Styling | Full CSS, Tailwind, grid, flexbox | Basic CSS only |
Modern layouts | Yes | Limited |
Rendering quality | Excellent | Good/Average |
Speed | Slower (Chrome startup) | Fast (pure PHP) |
JS support | Yes (limited) | No |
SVG support | Yes | Limited |
Setup complexity | Medium (Docker, Node, Chrome) | Very easy |
Docker/CI compatibility | Yes (with noSandbox) | Yes |
When to use?
- Spatie Laravel PDF (Browsershot): When you need beautiful, modern PDFs, want to use Tailwind, grid, flexbox, SVG, and your environment supports Chrome/Chromium (e.g., Docker, dedicated server). Use
withBrowsershot()
to customize options for Docker/CI. - barryvdh/laravel-dompdf: When you need simple PDFs, want a quick setup, and don't need advanced CSS or JS features (e.g., simple invoices, confirmations).
Summary
Both solutions have their place in the Laravel ecosystem. For modern projects where design and layout flexibility matter, choose Spatie Laravel PDF (with Browsershot). For simple use-cases where speed and simplicity are key, barryvdh/laravel-dompdf is sufficient.
Follow me on LinkedIn for more Laravel & DevOps tips and updates!
Would you like to learn more about PDF Generation in Laravel? Leave a comment below!