Guides › mPDF › Invoice example

Invoice example

Written for mPDF 8.2 on PHP 8.1+. Updated 2 November 2026.

Invoices are the most common reason people reach for mPDF. This example is complete: copy it into a project with mPDF installed (install guide) and run it.

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

$invoice = [
    'number' => 'INV-2026-041',
    'date' => '2 November 2026',
    'customer' => ['Lumen Bakery Ltd', '48 Market Street', 'Leeds LS1 6DT'],
    'items' => [
        ['Discovery workshop', 1, 450.00],
        ['Homepage and menu page design', 3, 520.00],
        ['Build and launch', 4, 480.00],
    ],
    'vat' => 0.20,
];

function e($value): string
{
    return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}

function money(float $amount): string
{
    return '£' . number_format($amount, 2);
}

$rows = '';
$subtotal = 0.0;
foreach ($invoice['items'] as [$description, $qty, $rate]) {
    $line = $qty * $rate;
    $subtotal += $line;
    $rows .= sprintf(
        '<tr><td>%s</td><td class="num">%s</td><td class="num">%s</td><td class="num">%s</td></tr>',
        e($description), e($qty), money($rate), money($line)
    );
}
$vat = round($subtotal * $invoice['vat'], 2);

$number = e($invoice['number']);
$date = e($invoice['date']);
$address = implode('<br>', array_map('e', $invoice['customer']));
$subtotalText = money($subtotal);
$vatText = money($vat);
$totalText = money($subtotal + $vat);

$html = <<<HTML
<style>
  body { font-family: dejavusans; font-size: 10pt; color: #222; }
  table { width: 100%; border-collapse: collapse; }
  .items th { background: #0e6b62; color: #fff; text-align: left; padding: 6px; }
  .items td { padding: 6px; border-bottom: 1px solid #ddd; }
  .num { text-align: right; }
  .totals { width: 45%; margin-left: 55%; margin-top: 10px; }
  .grand td { font-weight: bold; border-top: 2px solid #222; }
</style>
<htmlpagefooter name="foot">
  <div style="font-size: 8pt; text-align: center; color: #777;">{$number} · Page {PAGENO} of {nbpg}</div>
</htmlpagefooter>
<sethtmlpagefooter name="foot" value="on" />

<table><tr>
  <td><strong style="font-size: 16pt; color: #0e6b62;">Northwind Studio</strong><br>12 Harbour Lane, Bristol</td>
  <td class="num"><strong style="font-size: 20pt;">INVOICE</strong><br>{$number}<br>Issued {$date}</td>
</tr></table>
<p><strong>Bill to</strong><br>{$address}</p>
<table class="items">
  <thead><tr><th>Description</th><th class="num">Qty</th><th class="num">Rate</th><th class="num">Amount</th></tr></thead>
  <tbody>{$rows}</tbody>
</table>
<table class="totals">
  <tr><td>Subtotal</td><td class="num">{$subtotalText}</td></tr>
  <tr><td>VAT 20%</td><td class="num">{$vatText}</td></tr>
  <tr class="grand"><td>Total due</td><td class="num">{$totalText}</td></tr>
</table>
HTML;

$mpdf = new \Mpdf\Mpdf(['format' => 'A4', 'margin_bottom' => 20, 'tempDir' => sys_get_temp_dir() . '/mpdf']);
$mpdf->SetTitle('Invoice ' . $invoice['number']);
$mpdf->WriteHTML($html);
$mpdf->Output($invoice['number'] . '.pdf', \Mpdf\Output\Destination::DOWNLOAD);

What to notice

  • Every value is escaped with htmlspecialchars before it goes into HTML. Customer names like “Smith & Sons” would otherwise break the markup, and untrusted input could inject tags (see forms to PDF). The address lines are escaped one by one, then joined with <br>.
  • {PAGENO} and {nbpg} are left alone by PHP, because a { is only special to PHP when a $ follows it. mPDF replaces them when it lays out each page.
  • Layout is all tables, which mPDF renders reliably. No flexbox needed.
  • The footer is defined once and repeats on every page, with {PAGENO} and {nbpg} filled in (more).
  • Money is formatted in PHP, not in the template, so rounding is consistent between the totals and your accounts.

Try the layout in your browser

The same invoice design is in the examples gallery. Open it in HTML to PDF to tweak the CSS live before moving it into PHP.

To send the invoice by email instead of downloading it, see email a PDF.