Dompdf and wkhtmltopdf compared directly
Anyone who needs to generate invoices as PDF from a PHP application without a Node.js dependency almost always ends up with Dompdf or wkhtmltopdf. Both libraries only support a subset of the CSS features that a Tailwind template uses in the browser, which is why invoice layouts must be deliberately built on tables instead of flexbox.
Table of Contents
- 1. Why PHP applications often have to do without Node.js
- 2. Dompdf: pure PHP without an external dependency
- 3. wkhtmltopdf: an old WebKit base with better CSS support
- 4. Tailwind invoice layout without flexbox and grid
- 5. Practical example: rendering an invoice with Dompdf
- 6. Practical example: the same template with wkhtmltopdf
- 7. Fonts and number formatting in both libraries
- 8. Integration into Symfony and invoice archiving
- 9. Dompdf and wkhtmltopdf compared directly
- 10. Summary
- 11. FAQ
1. Why PHP applications often have to do without Node.js
Many PHP applications, especially Symfony projects or Magento stores, run in server environments where a Node.js runtime is deliberately not installed, whether due to security policies, deployment complexity, or because the operations team prefers a pure PHP environment. In such cases, Puppeteer-based PDF generation with real headless Chrome is off the table, because it necessarily requires a Node.js runtime and a complete Chromium browser. For PDF invoices in pure PHP environments, PHP-native libraries like Dompdf or external binary tools like wkhtmltopdf remain the practical options.
Both approaches follow the same basic idea as Puppeteer: an HTML template styled with Tailwind CSS is used as input and converted into a PDF document. The decisive difference lies in the feature set of the respective CSS engine. Neither Dompdf nor wkhtmltopdf supports the full modern CSS feature set that Chrome offers, which is why a Tailwind invoice template must be deliberately restricted to the CSS properties supported by both libraries.
2. Dompdf: pure PHP without an external dependency
Dompdf is a library written entirely in PHP that parses HTML and CSS directly within the PHP process and renders it into a PDF document, without any external programs or system dependencies. This makes Dompdf particularly attractive for shared hosting environments or restrictive server configurations where no additional binaries may be installed. The Composer command composer require dompdf/dompdf is enough for installation, without needing system administrator rights or a separate process call.
The downside of this pure PHP implementation: Dompdf's CSS engine supports neither flexbox nor CSS grid, and modern CSS selectors like :has() or :nth-child() in more complex combinations are only partially correctly interpreted. A Tailwind invoice template that uses flex or grid must be completely switched to table-based layout for Dompdf, similar to email client compatibility for Outlook.
# Install Dompdf via Composer, no external binary required
composer require dompdf/dompdf
3. wkhtmltopdf: an old WebKit base with better CSS support
wkhtmltopdf isn't a pure PHP package but a standalone command-line tool based on an embedded, older WebKit rendering engine. PHP applications call this binary via a process call, usually through a wrapper like knplabs/knp-snappy, which conveniently provides the command-line options as a PHP API. Because WebKit is a real browser engine, wkhtmltopdf supports significantly more modern CSS than Dompdf, including partial flexbox support in newer versions.
The downside: the WebKit version embedded in wkhtmltopdf hasn't been updated in years, and the official project is now considered discontinued. CSS features added after the last WebKit snapshot, such as CSS grid or container queries, therefore don't work reliably. Additionally, wkhtmltopdf requires installing a system binary, which can become a problem in heavily restricted hosting environments without root access.
4. Tailwind invoice layout without flexbox and grid
For the same Tailwind invoice template to work with both Dompdf and wkhtmltopdf, a table-based layout for all structural elements is recommended from the start: header with company logo and billing address, line item table with quantity, description, unit price, and total price, as well as a footer with tax rates and payment terms. Tailwind classes for cell padding, text alignment, and border colors can be applied to <table>, <tr>, and <td> elements without relying on modern layout mechanisms.
For the grand total at the end of the invoice, which usually sits right-aligned below the line item table, a simple table row with text-right works reliably in both libraries, while a justify-end layout built with flexbox would be completely ignored in Dompdf. The rule of thumb for maximum compatibility: every structural arrangement is solved with tables, while Tailwind classes are used exclusively for colors, spacing, font sizes, and borders.
<!-- invoice.html — table-based layout compatible with both Dompdf and wkhtmltopdf -->
<table class="w-full border-collapse" cellpadding="0" cellspacing="0">
<thead>
<tr class="bg-slate-900 text-white">
<th class="p-3 text-left">Description</th>
<th class="p-3 text-right">Qty</th>
<th class="p-3 text-right">Unit price</th>
<th class="p-3 text-right">Total</th>
</tr>
</thead>
<tbody>
<tr class="border-b border-slate-200">
<td class="p-3">Web development, sprint 12</td>
<td class="p-3 text-right">40</td>
<td class="p-3 text-right">95.00 EUR</td>
<td class="p-3 text-right font-semibold">3,800.00 EUR</td>
</tr>
</tbody>
</table>
5. Practical example: rendering an invoice with Dompdf
Calling Dompdf from PHP is straightforward: an instance is created, HTML is passed via loadHtml(), the paper format is set via setPaper(), and render() generates the document in memory. Before the invoice template is loaded into Dompdf, the compiled Tailwind CSS must either be inline in the <head> or embedded as a string, since Dompdf doesn't reliably resolve external stylesheet references over HTTP from the file system.
An important detail: Dompdf needs explicit UTF-8 configuration and a font that covers special characters for umlauts and accented characters in German invoices. Dompdf's default fonts reliably support European special characters; for custom web fonts from the Tailwind theme, the font file must additionally be registered via Dompdf\Options::setFontDir(), since Dompdf doesn't automatically load web fonts from the CSS like a browser does.
<?php
declare(strict_types=1);
use Dompdf\Dompdf;
use Dompdf\Options;
// Configure Dompdf for UTF-8 invoices with Tailwind-compiled CSS
$options = new Options();
$options->set('isRemoteEnabled', false);
$options->set('defaultFont', 'DejaVu Sans');
$dompdf = new Dompdf($options);
$html = file_get_contents(__DIR__ . '/dist/invoice.html');
$dompdf->loadHtml($html, 'UTF-8');
$dompdf->setPaper('A4', 'portrait');
$dompdf->render();
file_put_contents(__DIR__ . '/output/invoice-2026-0142.pdf', $dompdf->output());
6. Practical example: the same template with wkhtmltopdf
For wkhtmltopdf, the knp-snappy wrapper handles communication with the command-line program and provides a PHP class that conveniently accepts options like margins, paper format, and headers and footers. Because wkhtmltopdf internally starts a full WebKit instance, it additionally supports @page rules for page margins directly in CSS, which only works to a limited extent in Dompdf. For invoices with continuous page numbers across multiple line items, this is a noticeable advantage.
One practical difference: wkhtmltopdf loads local image files via relative paths more reliably than Dompdf, as long as the working directory is set correctly, because the WebKit engine uses the same path resolution as a normal browser. For company logos that sit in the same directory as the HTML template, this means less configuration effort than with Dompdf, where image paths often have to be given as absolute file system paths.
<?php
declare(strict_types=1);
use Knp\Snappy\Pdf;
// wkhtmltopdf via the Snappy wrapper — supports @page CSS rules
$snappy = new Pdf('/usr/local/bin/wkhtmltopdf');
$snappy->setOption('page-size', 'A4');
$snappy->setOption('margin-top', '15mm');
$snappy->setOption('margin-bottom', '15mm');
$snappy->setOption('encoding', 'UTF-8');
$snappy->generateFromHtml(
file_get_contents(__DIR__ . '/dist/invoice.html'),
__DIR__ . '/output/invoice-2026-0142.pdf'
);
7. Fonts and number formatting in both libraries
Invoices frequently contain numeric values that need to be displayed in a locale-specific format with the appropriate decimal and thousands separators. This formatting is ideally done in PHP before insertion into the Tailwind template, for example via number_format($amount, 2, ',', '.') for German formatting, instead of relying on client-side formatting, which isn't available in either PDF library anyway, since no JavaScript is executed.
With fonts there's another difference: Dompdf ships with a limited selection of embedded fonts, while wkhtmltopdf renders using the server's system fonts and theoretically offers more choice, but in practice depends on the consistency of the server configuration. For maximum control over appearance, in both cases it's recommended to ship a specific font file and explicitly include it in the CSS via @font-face with a local file path, instead of relying on system fonts.
<?php
declare(strict_types=1);
// Format amounts in the target locale before inserting them into the Tailwind template
$formatted = number_format($lineItem->getTotal(), 2, '.', ',') . ' EUR';
/* Embed a local font file explicitly, do not rely on system fonts */
@font-face {
font-family: "Inter";
src: url("fonts/inter-regular.ttf") format("truetype");
}
body { font-family: "Inter", "DejaVu Sans", sans-serif; }
8. Integration into Symfony and invoice archiving
In Symfony projects, both Dompdf and wkhtmltopdf can be registered as a service called from a controller or a console command to generate invoices from order data. Twig handles rendering the HTML template with the inserted order data, before the resulting HTML is passed to Dompdf or wkhtmltopdf for PDF conversion. This separation keeps the invoicing logic in Twig templates and the PDF conversion as a swappable infrastructure building block.
For legally required invoice archiving, it's important to store the generated PDF immutably, usually in a dedicated storage folder with a sequential invoice number in the filename, and additionally record a checksum hash of the file in the database. This later allows proving that an archived invoice hasn't been altered since creation, regardless of whether Dompdf or wkhtmltopdf was used for the original generation.
<?php
declare(strict_types=1);
// Store the PDF immutably and record a checksum for audit purposes
$pdfContent = $dompdf->output();
$path = sprintf('/var/invoices/%s.pdf', $invoiceNumber);
file_put_contents($path, $pdfContent);
$invoice->setChecksum(hash('sha256', $pdfContent));
$invoice->setArchivedAt(new \DateTimeImmutable());
$entityManager->flush();
9. Dompdf and wkhtmltopdf compared directly
The following overview summarizes the practical differences that are decisive when choosing between the two libraries for Tailwind-based PDF invoices.
| Criterion | Dompdf | wkhtmltopdf |
|---|---|---|
| Installation | Pure Composer package | Additional system binary needed |
| CSS support | No flexbox, no grid | Partial flexbox, no grid |
| Image path resolution | Absolute paths usually required | Relative paths work more reliably |
| Project status | Actively maintained | Officially discontinued |
For new projects without legacy baggage, Dompdf is usually the more pragmatic choice thanks to its simpler installation and active development, provided the invoice template consistently uses table layout instead of flexbox. wkhtmltopdf remains relevant for existing projects already built on it, or when @page CSS rules are needed for more complex headers and footers across multiple pages.
Mironsoft
Tailwind CSS, PDF invoices, and document generation for Symfony and Magento projects
Invoices as PDF, reliably from pure PHP?
We build Tailwind-based invoice templates for Dompdf or wkhtmltopdf, including correct number formatting, font embedding, and legally compliant archiving.
Invoice template
Table-based Tailwind layout compatible with Dompdf and wkhtmltopdf
PHP integration
Connecting to Symfony controllers and console commands
Archiving
Immutable storage with checksums for invoicing compliance
10. Summary
Dompdf and wkhtmltopdf are the obvious options for PDF invoices in pure PHP environments without a Node.js dependency. Both only support a subset of the modern CSS that Tailwind normally generates, which is why invoice templates must consistently rely on table-based layout instead of flexbox or grid. Dompdf scores with simple Composer installation and active development, while wkhtmltopdf understands somewhat more modern CSS thanks to its WebKit base, but requires an additional system binary and is officially no longer developed.
For new projects, Dompdf is usually the more pragmatic choice. Regardless of the chosen library: correct UTF-8 configuration, explicitly embedded fonts, and server-side preformatted numeric values are mandatory for a Tailwind invoice template to look reliable and professional in both PDF libraries.
For teams with an existing Node.js infrastructure anyway, Puppeteer remains the more faithful alternative with a full modern CSS feature set, while Dompdf and wkhtmltopdf play to their strengths wherever a pure PHP environment is mandatory.
PDF Invoices with Tailwind CSS — Key Takeaways
Dompdf
Pure Composer package without system dependency, but no flexbox or grid support.
wkhtmltopdf
WebKit-based with better CSS support, but requires an additional system binary.
Tailwind invoice layout
Consistently table-based, Tailwind classes only for colors, spacing, and typography.
Practical details
UTF-8 configuration, local font files, and server-side number formatting are mandatory in both libraries.