If you upgraded sylius/invoicing-plugin to 2.3 last month, the upgrade note had a paragraph most people scroll past. The legacy PDF generator, and the Gaufrette storage it writes to, will be removed in 3.0. sylius/refund-plugin 2.2 carries the same paragraph, word for word, about credit memos. Both plugins still default to the legacy path. So if you never touched pdf_generator in your config, both your invoices and your credit memos are on it today.
The forced part of the move is small. The optional part, getting the wkhtmltopdf binary out of your container, turns out to be one line of YAML per document type. We ran it against the stock invoice template, and two defaults change under you when you do. Those are most of this page.
What 3.0 actually removes
Read the notes closely, because it’s narrower than it sounds. 3.0 removes the sylius_invoicing.pdf_generator.legacy and sylius_refund.pdf_generator.legacy options, the old generator behind them, and Gaufrette. Both options default to true and are deprecated now. What replaces them is sylius/pdf-generation-bundle (v1.0.0, March 2026), which puts the PDF engine behind a named adapter, chosen per context.
What 3.0 does not remove is wkhtmltopdf. The bundle ships a knp_snappy adapter, and when you switch the legacy flag off, both plugins register their context with exactly that adapter. So if your invoices print fine and you only want to be ready for 3.0, step 1 below is the whole job. Keeping wkhtmltopdf is a legitimate answer for a lot of stores.
Step 1: turn the legacy generator off, and keep finding old files
# config/packages/sylius_invoicing.yaml
sylius_invoicing:
pdf_generator:
legacy: false
# config/packages/sylius_refund.yaml
sylius_refund:
pdf_generator:
legacy: false
The one trap here is storage, and the notes spell it out. With the integration on, files still go through Gaufrette by default. Each plugin also provides a Flysystem storage pointing at the same directory (%sylius_invoicing.invoice_save_path%, and the credit memo equivalent), and that becomes the default in 3.0. Switch now, while you can check that the invoices you issued last year still download:
# config/packages/sylius_pdf_generation.yaml
sylius_pdf_generation:
contexts:
sylius_invoicing:
storage:
type: flysystem
filesystem: sylius_invoicing.storage.invoice
sylius_refund:
storage:
type: flysystem
filesystem: sylius_refund.storage.credit_memo
If you store invoices on S3, redefine those two storages under flysystem.storages rather than pointing the context at something new. Same names, different adapter, and existing paths still resolve.
Step 2, optional: change the engine
The contexts are named sylius_invoicing and sylius_refund. Each plugin prepends its context with adapter: knp_snappy, which means anything you put in your own config wins. To render invoices with dompdf, which is pure PHP and needs no binary:
composer require dompdf/dompdf
sylius_pdf_generation:
contexts:
sylius_invoicing:
adapter: dompdf
sylius_refund:
adapter: dompdf
That’s the change. We checked it rather than trusting the README: a Symfony 7.4 kernel on PHP 8.3 with sylius/pdf-generation-bundle 1.0.0 and dompdf/dompdf 3.1.6, the sylius_invoicing context registered the way the plugin registers it, then the plugin’s own v2.3.0 invoice template rendered through TwigToPdfRendererInterface with a two-line test invoice. It came back as a correct PDF in about a tenth of a second, logo, buyer and seller boxes, line items, tax table and all. Delete the adapter: dompdf line and the container refuses to build:
The "knp_snappy" adapter is configured for the "sylius_invoicing" context,
but its required dependency "Knp\Snappy\GeneratorInterface" is not installed.
So the override really is what moves the context. We ran the invoice side only. Credit memos go through the identical mechanism under sylius_refund, but we haven’t rendered the refund template, so render one of yours before you ship.
Gotcha 1: the paper size changes
The first render came back at 612 × 792 points. That is US Letter. wkhtmltopdf defaults to A4, and dompdf defaults to letter (it’s the defaultPaperSize in dompdf’s Options). The bundle’s dompdf provider creates a plain new Dompdf(), so nothing in between sets it. A US store may be quietly pleased. A European one will start issuing Letter invoices without any error to tell them.
The bundle’s fix is an options processor scoped to the adapter and the context. This one set A4 in our run (the MediaBox became 595.28 × 841.89):
<?php
namespace App\Pdf;
use Sylius\PdfGenerationBundle\Core\Attribute\AsPdfOptionsProcessor;
use Sylius\PdfGenerationBundle\Core\Processor\OptionsProcessorInterface;
// no context: runs for every context that uses dompdf
#[AsPdfOptionsProcessor(adapter: 'dompdf')]
final class A4PaperProcessor implements OptionsProcessorInterface
{
public function process(object $generator, string $context = 'default'): void
{
/** @var \Dompdf\Dompdf $generator */
$generator->setPaper('A4', 'portrait');
}
}
Leaving context out applies it to invoices and credit memos both. Add context: 'sylius_invoicing' if only one of them should change. The attribute isn’t repeatable, so for two specific contexts, register the class twice with the sylius_pdf_generation.options_processor tag instead.
Gotcha 2: a logo given as a file path disappears
The stock layout is fine here. The plugin passes the logo to the template twice, as invoiceLogo (a base64 data: URI) and as invoiceLogoPath (a file on disk), and the template uses the data URI first. dompdf renders that without complaint.
But plenty of customised templates use the path, because under wkhtmltopdf it works: the plugin passes that file to wkhtmltopdf’s allow-list. That allow-list is an options processor tagged for knp_snappy only. dompdf has its own rule, a chroot that defaults to dompdf’s own install directory, so a logo under your project’s public/ is outside it. We rendered the same invoice with the data URI blanked out: the PDF came back with no error and no image at all. Go back to invoiceLogo in your template, or widen chroot in an options processor like the one above.
Which engine, then
| Adapter | Needs | Pick it when |
|---|---|---|
knp_snappy |
the wkhtmltopdf binary | your PDFs are fine and you only want to be 3.0-ready. Step 1 and stop. |
dompdf |
nothing, it’s PHP | your template is tables and blocks, like the stock one. Mind the two gotchas. |
gotenberg |
a running Gotenberg container | your template leans on flexbox, grid or web fonts. dompdf describes itself as CSS 2.1 with some CSS3, and it shows on modern layouts. |
You can mix them, which is the point of contexts: dompdf for invoices, Gotenberg for a heavier credit memo, or the other way round. We didn’t run Gotenberg for this piece, so that row is from the bundle’s README, not from a render.
Where we fit: mostly, we don’t
Every adapter in that bundle takes the HTML your Twig template produced and turns it into a PDF. iReceipt PRO doesn’t do that. It renders a template that lives on our side from JSON you send, and there is no raw-HTML endpoint, so we aren’t a fourth adapter and we’d be the wrong tool for a store that wants to keep its Twig template. For that store, dompdf or Gotenberg is the answer.
The case where we do fit is narrower: you want the invoice and credit memo layout out of Twig entirely, so that nobody on your team maintains it. That trade-off has its own page, the seam behind Sylius invoice PDFs, including when it isn’t worth it.