# Dompdf alternative for PHP

Dompdf alternative for PHP. Dompdf lacks flexbox and grid and slows on big tables. Compare mPDF and Browsershot, or render Chrome-quality PDFs.

Dompdf is the default answer for "HTML to PDF in PHP". It's pure PHP, installs with Composer and works well for simple documents. Teams often hit a wall when the HTML uses modern CSS or gets long.

This page covers the usual pain points, the main PHP alternatives, and how to call a Chrome-based API from PHP and Laravel.

**Quick answer.** Send your HTML to a hosted Chromium API and get the PDF back:

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

## Common Dompdf problems

These are the issues that usually push teams away from Dompdf.

- **No flexbox or grid.** Dompdf implements its own layout engine, mostly CSS 2.1 with some CSS3. Layouts built with `display: flex` or `display: grid` collapse into stacked blocks.
- **Limited CSS3.** Many newer properties and selectors are partial or missing, so a template that looks right in Chrome looks different in the PDF.
- **Big tables are slow.** Long tables use a lot of memory and render time, and can hit PHP's `memory_limit` or `max_execution_time`.
- **Fonts take setup.** Custom fonts need to be registered or loaded with `@font-face` and a writable font cache. Missing glyphs show up as `?`.
- **No JavaScript.** Charts drawn with Chart.js or similar never render.

## PHP alternatives

Here are the three main options PHP developers move to.

**mPDF** is also pure PHP. It handles headers, footers, right-to-left and CJK text well. It also uses its own layout engine, so flexbox and grid have the same problem.

**Spatie Browsershot** drives real headless Chrome through Puppeteer, so CSS and JavaScript render like the browser. The cost is infrastructure. You need Node.js, Puppeteer, a Chrome binary and Chrome's system libraries on every server, which is awkward on shared hosting, slim Docker images and some serverless setups.

**A hosted Chrome API** gives Browsershot-style output with just an HTTP call from PHP.

## Comparison

This table compares the four options at a glance.

| | Dompdf | mPDF | Browsershot | html-pdf.com API |
|---|---|---|---|---|
| Engine | PHP layout engine | PHP layout engine | Headless Chrome | Headless Chromium (hosted) |
| Flexbox / grid | No | No | Yes | Yes |
| JavaScript | No | No | Yes | Yes |
| Server requirements | PHP extensions | PHP extensions | Node + Puppeteer + Chrome | None (HTTP) |
| Works offline | Yes | Yes | Yes | No |
| Cost | Free (LGPL) | Free (GPL) | Free (MIT) + servers | 1 credit per page |

## When Dompdf is the better choice

Dompdf is still the right tool in a few cases.

- Simple, table-based documents (basic invoices, letters) that you design for Dompdf from the start.
- Environments without outbound network access, or where HTML must never leave your server.
- Low volume, where adding a paid service isn't worth it.

If your templates already work in Dompdf and you're happy with them, there's no reason to switch.

## Calling a Chrome API from PHP

These examples show how to render a PDF from plain PHP and from Laravel.

### Plain PHP (cURL)

```php
<?php
$html = '<div style="display:flex;justify-content:space-between"><h1>Invoice #042</h1><p>Total: $120.00</p></div>';

$ch = curl_init('https://html-pdf.com/v1/pdf');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 40,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('HTML_PDF_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'html' => $html,
        'format' => 'A4',
        'landscape' => true,
        'margin' => '15mm',
    ]),
]);

$pdf = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($status !== 200) {
    throw new RuntimeException("PDF failed ($status): $pdf");
}
file_put_contents('invoice.pdf', $pdf);
```

### Laravel (Http client)

```php
use Illuminate\Support\Facades\Http;

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

    $response = Http::withToken(env('HTML_PDF_KEY'))
        ->timeout(40)
        ->post('https://html-pdf.com/v1/pdf', [
            'html' => $html,
            'format' => 'A4',
            'margin' => '15mm',
            'max_pages' => 20,
        ]);

    abort_unless($response->successful(), 502, 'PDF generation failed');

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

In a real app, read the key through `config()` rather than `env()` so it still works after `php artisan config:cache`. Your Blade views can keep using Tailwind, flexbox or grid.

Images and fonts must use absolute `https://` URLs or data URIs, because the renderer can't reach `localhost`.

## Mapping Dompdf calls

This table shows the html-pdf.com option for each common Dompdf call.

| Dompdf | html-pdf.com |
|---|---|
| `$dompdf->setPaper('A4', 'landscape')` | `'format' => 'A4', 'landscape' => true` |
| `setPaper('letter', 'portrait')` | `'format' => 'Letter'` |
| `setPaper([0, 0, 612, 1008])` (points) | `'width' => '8.5in', 'height' => '14in'` |
| `isRemoteEnabled` | Not needed; public `https://` assets load |
| `$dompdf->stream('file.pdf')` | `'filename' => 'file.pdf'` and return the body |
| `$canvas->page_text(...)` page numbers | `footer_html` with `pageNumber` / `totalPages` |
| `@page { margin: ... }` | Keep it with `prefer_css_page_size`, or use `margin` |

For page numbers, add a footer and leave room for it with a bottom margin.

```php
'margin' => ['top' => '15mm', 'right' => '12mm', 'bottom' => '20mm', 'left' => '12mm'],
'footer_html' => '<div style="font-size:9px;width:100%;text-align:center">Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>',
```

## Or skip the server

html-pdf.com lets you get Chrome-quality PDFs without installing Node, Puppeteer or Chrome on your PHP servers. It's a hosted HTML and URL to PDF API on headless Chromium.

Send `POST https://html-pdf.com/v1/pdf` with a Bearer key and `{"html": "..."}` or `{"url": "https://..."}`. You get back a PDF with selectable text, as binary, base64 or a 24-hour link.

1 credit = 1 PDF page. You get 100 free credits once, then packs run from $10 for 1,000 pages to $250 for 50,000 ($0.01 to $0.005 per page), and purchased credits never expire. A [remote MCP server](https://html-pdf.com/pdf-mcp-server) is also available for AI assistants.

## FAQ

### What is the best Dompdf alternative for flexbox and grid?

A Chrome-based renderer. Browsershot runs headless Chrome on your own server, and html-pdf.com runs Chromium as a hosted API, so flexbox, grid and JavaScript render like the browser. mPDF has the same layout limits as Dompdf.

### How do I add page numbers when moving off Dompdf?

Replace `$canvas->page_text(...)` with a `footer_html` that contains `<span class="pageNumber"></span>` and `<span class="totalPages"></span>`. Leave a bottom margin so the footer has room.

### Is there a free Dompdf alternative?

mPDF and Browsershot are free open-source libraries, though Browsershot needs Chrome on your servers. html-pdf.com gives you 100 free credits once with no card, and 1 credit is 1 PDF page.

### Can I use html-pdf.com from Laravel?

Yes. Render your Blade view to HTML and post it to `https://html-pdf.com/v1/pdf` with `Http::withToken(...)`, as in the example above.

[Get a free API key](https://html-pdf.com/login) · [API docs](https://html-pdf.com/docs) · [HTML to PDF in PHP](https://html-pdf.com/html-to-pdf-php) · [HTML to PDF in Laravel](https://html-pdf.com/html-to-pdf-laravel) · [Page breaks](https://html-pdf.com/html-to-pdf-page-breaks)
