HTML to PDF in PHP (and Laravel)

HTML to PDF in PHP (and Laravel)

Convert HTML to PDF in PHP with Dompdf, mPDF or Browsershot, with working code, plus a one-call API option for plain PHP and Laravel.

PHP has several ways to turn HTML into a PDF. The right one depends on how modern your CSS is.

Pure-PHP libraries are easy to install but don't support flexbox or grid. Browser-based tools render like Chrome but need Chrome on the server. This guide covers both, then a third option that needs neither.

Quick answer. Send your HTML to the html-pdf.com API and save the bytes it returns. No Chrome or PDF library is needed on your server.

curl -X POST https://html-pdf.com/v1/pdf \
  -H "Authorization: Bearer $HTML_PDF_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html": "<h1>Invoice #042</h1>"}' \
  -o invoice.pdf

Which PHP HTML to PDF library?

This table shows how each tool renders, what CSS it handles and what you need to install.

Tool How it renders Modern CSS (flexbox, grid) JavaScript Install
Dompdf Own PHP layout engine No (mostly CSS 2.1) No composer require dompdf/dompdf
mPDF Own PHP layout engine No No composer require mpdf/mpdf
Spatie Browsershot Headless Chrome via Puppeteer Yes Yes Composer + Node + Puppeteer + Chrome
wkhtmltopdf (via Snappy) Old QtWebKit Partial Old engine Binary, project archived
html-pdf.com API Headless Chromium, hosted Yes Yes Nothing (cURL or Guzzle)

Option 1: Dompdf

Dompdf is the most used pure-PHP option. Use it for simple, table-based documents.

<?php
require 'vendor/autoload.php';

use Dompdf\Dompdf;
use Dompdf\Options;

$options = new Options();
$options->set('isRemoteEnabled', true); // allow https images and fonts

$dompdf = new Dompdf($options);
$dompdf->loadHtml('<h1>Invoice #042</h1><p>Total: $120.00</p>');
$dompdf->setPaper('A4', 'portrait');
$dompdf->render();

file_put_contents('invoice.pdf', $dompdf->output());
// or stream to the browser: $dompdf->stream('invoice.pdf', ['Attachment' => false]);

Dompdf has no flexbox or grid, so layouts built with modern CSS collapse. Large tables can be slow and use a lot of memory, and you need to register custom fonts yourself. Only enable isRemoteEnabled if you trust the HTML.

Option 2: mPDF

mPDF works from PHP alone. It handles headers, footers and right-to-left or CJK text well.

<?php
require 'vendor/autoload.php';

$mpdf = new \Mpdf\Mpdf(['format' => 'A4', 'margin_top' => 15, 'margin_bottom' => 20]);
$mpdf->SetHTMLFooter('<div style="text-align:center;font-size:9px">Page {PAGENO} of {nbpg}</div>');
$mpdf->WriteHTML('<h1>Invoice #042</h1><p>Total: $120.00</p>');
$mpdf->Output('invoice.pdf', \Mpdf\Output\Destination::FILE);

Like Dompdf, it uses its own layout engine, so a page that looks right in Chrome may not look the same in the PDF.

Option 3: Browsershot (headless Chrome)

Browsershot gives you Chrome-quality PDFs. It calls Puppeteer from PHP, so the PDF matches Chrome's print output.

composer require spatie/browsershot
npm install puppeteer
<?php
use Spatie\Browsershot\Browsershot;

Browsershot::html('<h1>Invoice #042</h1>')
    ->format('A4')
    ->margins(15, 15, 15, 15)
    ->showBackground()
    ->save('invoice.pdf');

The catch is running it. Your PHP servers now need Node.js, Puppeteer, a Chrome build with its system libraries, and enough memory to run a browser per request. On shared hosting that often isn't possible.

Option 4: one API call, no Chrome on your server

html-pdf.com renders your HTML in headless Chromium and returns the PDF. You get Browsershot-quality output with nothing but PHP's cURL extension.

<?php
$html = '<html><head><style>
  .row { display: flex; justify-content: space-between }
</style></head><body>
  <h1>Invoice #042</h1>
  <div class="row"><span>Design work</span><span>$120.00</span></div>
</body></html>';

$ch = curl_init('https://html-pdf.com/v1/pdf');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 60,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('HTML_PDF_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'html' => $html,
        'format' => 'A4',
        'margin' => '15mm',
        'footer_html' => '<div style="font-size:9px;width:100%;text-align:center">'
            . 'Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>',
    ]),
]);
$pdf = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($status !== 200) {
    $err = json_decode($pdf, true)['error'] ?? [];
    throw new RuntimeException(($err['code'] ?? 'error') . ': ' . ($err['message'] ?? ''));
}
file_put_contents('invoice.pdf', $pdf);

To convert a public web page instead, send 'url' => 'https://example.com'. Add 'response' => 'url' to get JSON with a download link valid for 24 hours.

Laravel: render a Blade view to PDF

Here you get a Laravel route that returns a Blade view as a PDF. Laravel's HTTP client keeps it short, so render the view to HTML and send it.

<?php
// routes/web.php
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Route;

Route::get('/invoices/{invoice}/pdf', function (App\Models\Invoice $invoice) {
    $html = view('invoices.pdf', ['invoice' => $invoice])->render();

    $response = Http::withToken(config('services.html_pdf.key'))
        ->timeout(60)
        ->post('https://html-pdf.com/v1/pdf', [
            'html' => $html,
            'format' => 'A4',
            'margin' => '15mm',
            'max_pages' => 20, // fail without charge if it runs longer
        ]);

    abort_unless($response->successful(), 502, $response->json('error.message'));

    return response($response->body(), 200, [
        'Content-Type' => 'application/pdf',
        'Content-Disposition' => "inline; filename=\"invoice-{$invoice->id}.pdf\"",
    ]);
});

Add 'html_pdf' => ['key' => env('HTML_PDF_KEY')] to config/services.php. Blade's double-curly-brace echo escapes output, which keeps customer data from breaking your HTML. Use absolute https:// URLs for images and CSS in the view, or inline them, since the renderer can't reach localhost or private networks.

Get an API key

One call gives you a key.

curl -X POST https://html-pdf.com/v1/signup -H "Content-Type: application/json" -d '{"email": "you@example.com"}'

Open the link we email you to confirm your address. That adds 100 free credits, and 1 credit = 1 PDF page. Failed renders cost nothing, and purchased credits never expire. Read the response headers X-Pages and X-Credits-Remaining to track usage.

Coming from wkhtmltopdf or Snappy? See wkhtmltopdf alternative. For pagination, see page breaks. The full reference is in the API docs.

FAQ

How do I convert HTML to PDF in PHP?

Use Dompdf or mPDF for simple layouts, or Browsershot if you can run Chrome on the server. For Chrome-quality output with only cURL, POST your HTML to https://html-pdf.com/v1/pdf and save the response.

Does Dompdf support flexbox or grid?

No. Dompdf and mPDF mostly support CSS 2.1, so flexbox and grid layouts break. Browsershot and the html-pdf.com API render with Chromium, so modern CSS works.

Is there a free HTML to PDF API for PHP?

html-pdf.com gives you 100 free credits once, with no card needed. 1 credit = 1 PDF page, and failed renders cost nothing.

Do credits expire?

No. Purchased credits never expire. Packs go from $10 for 1,000 pages to $250 for 50,000 pages.

This page as markdown · text