HTML to PDF in Laravel/PHP: The Simplest Integration
DomPDF can't handle modern CSS. Snappy wraps archived wkhtmltopdf. Here's how to generate PDFs in Laravel with a single HTTP request to a $5/year API — no packages, no binaries.
Laravel developers typically reach for one of two PDF packages: DomPDF (via barryvdh/laravel-dompdf) or Snappy (via barryvdh/laravel-snappy). Both have significant problems in 2026.
- DomPDF: Pure PHP renderer. No flexbox, no grid, limited CSS support. Tables break with complex layouts. Fine for simple documents, painful for anything modern.
- Snappy: A wrapper around wkhtmltopdf, which was archived in January 2023. No security updates, no modern CSS, no modern JavaScript.
There's a simpler option: skip the packages entirely and make an HTTP request to a PDF API.
The API approach
Send your rendered Blade template as HTML to the API. Get a PDF URL back. No composer packages, no binary dependencies, no system library requirements.
// app/Http/Controllers/InvoiceController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
class InvoiceController extends Controller
{
public function downloadPdf(string $invoiceId)
{
$invoice = Invoice::findOrFail($invoiceId);
// Render the Blade template to HTML
$html = view('invoices.pdf', compact('invoice'))->render();
// Convert to PDF via API
$response = Http::withHeaders([
'Authorization' => 'Bearer ' . config('services.pdf.api_key'),
'Content-Type' => 'text/html',
])->withBody($html, 'text/html')
->post('https://htmltopdfconverter.com.au/api/programmatic/pdf/generate');
$pdfUrl = $response->json('pdfs.0.url');
return redirect($pdfUrl);
}
}
Configuration
Add your API key to .env:
PDF_API_KEY=your_api_key_here
And register it in config/services.php:
'pdf' => [
'api_key' => env('PDF_API_KEY'),
],
Blade template example
<!-- resources/views/invoices/pdf.blade.php -->
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; margin: 40px; color: #333; }
.header { display: flex; justify-content: space-between; margin-bottom: 30px; }
.company-name { font-size: 24px; font-weight: bold; }
table { width: 100%; border-collapse: collapse; margin: 20px 0; }
th { background: #f8f8f8; padding: 10px; text-align: left; border-bottom: 2px solid #ddd; }
td { padding: 10px; border-bottom: 1px solid #eee; }
.text-right { text-align: right; }
.total { font-weight: bold; font-size: 18px; border-top: 2px solid #333; }
</style>
</head>
<body>
<div class="header">
<div class="company-name">{{ config('app.name') }}</div>
<div>Invoice #{{ $invoice->number }}<br>{{ $invoice->date->format('d M Y') }}</div>
</div>
<p><strong>Bill to:</strong> {{ $invoice->customer->name }}</p>
<table>
<thead>
<tr><th>Description</th><th class="text-right">Qty</th><th class="text-right">Price</th><th class="text-right">Total</th></tr>
</thead>
<tbody>
@foreach($invoice->items as $item)
<tr>
<td>{{ $item->description }}</td>
<td class="text-right">{{ $item->quantity }}</td>
<td class="text-right">$@{{ number_format($item->price, 2) }}</td>
<td class="text-right">$@{{ number_format($item->total, 2) }}</td>
</tr>
@endforeach
<tr class="total">
<td colspan="3">Total</td>
<td class="text-right">$@{{ number_format($invoice->total, 2) }}</td>
</tr>
</tbody>
</table>
</body>
</html>
Because the API uses Chromium, your Blade template can use modern CSS — flexbox, grid, custom fonts, backgrounds. Everything that works in Chrome works in the PDF.
Multipart file upload (alternative)
If you prefer sending files rather than raw HTML:
$response = Http::withHeaders([
'Authorization' => 'Bearer ' . config('services.pdf.api_key'),
])->attach('files', $html, 'invoice.html', ['Content-Type' => 'text/html'])
->post('https://htmltopdfconverter.com.au/api/programmatic/pdf/generate');
Why this beats DomPDF and Snappy
| API approach | DomPDF | Snappy | |
|---|---|---|---|
| CSS support | Full (Chromium) | Limited (no flexbox/grid) | Outdated (QtWebKit) |
| Dependencies | None | Composer package | wkhtmltopdf binary |
| Maintenance | Managed | Active | Archived upstream |
| Rendering quality | Browser-identical | Approximate | Outdated engine |
| Cost | $5 AUD/year | Free | Free (with caveats) |
Get started
- Create an account (30 seconds)
- Subscribe from your dashboard ($5 AUD/year)
- Generate an API key and add it to your
.env - Copy the controller code above
- Create your Blade template and route
Full API docs — request formats, error codes, rate limits, and examples in PHP, Node.js, and Python.
FAQ
Do I need to install any composer packages?
No. Laravel's built-in Http facade handles the API call. No additional packages required.
Can I use this with Livewire or Inertia?
Yes. The PDF generation happens server-side in a controller or action. It doesn't matter how the request is triggered on the frontend — Livewire, Inertia, or a plain anchor tag all work.
What about DomPDF — is it really that limited?
DomPDF works well for simple, table-based layouts. But it has no flexbox or grid support, limited float handling, and struggles with complex CSS. If your templates are basic, DomPDF is fine. If they use modern CSS, it won't render them correctly.
Should I migrate from Snappy?
Yes. Snappy depends on wkhtmltopdf, which was archived in 2023 with no security updates. Continuing to use it means running unpatched software with known vulnerabilities.