NextPDF Symfony quickstart
At a glance
Section titled “At a glance”Inject PdfFactory, build a Document, and return it through PdfResponse.
When you need background generation, dispatch a GeneratePdfMessage to a
Messenger transport.
Step 1 — Generate a PDF in a controller
Section titled “Step 1 — Generate a PDF in a controller”Inject NextPDF\Symfony\Service\PdfFactory. Its create() method returns a
fresh NextPDF\Core\Document. It applies your configured defaults for the
creator, the author, and the language. Return the document through
NextPDF\Symfony\Http\PdfResponse.
<?php
declare(strict_types=1);
namespace App\Controller;
use NextPDF\Symfony\Http\PdfResponse;use NextPDF\Symfony\Service\PdfFactory;use Symfony\Component\HttpFoundation\Response;use Symfony\Component\Routing\Attribute\Route;
final class InvoiceController{ #[Route('/invoice/{number}', name: 'invoice_pdf')] public function download(PdfFactory $pdf, string $number): Response { $doc = $pdf->create(); $doc->addPage(); $doc->cell(0, 10, "Invoice #{$number}", newLine: true); $doc->cell(0, 10, 'Thank you for your business.');
return PdfResponse::download($doc, "invoice-{$number}.pdf"); }}PdfResponse::download() returns a Symfony\Component\HttpFoundation\Response.
It sets Content-Type: application/pdf, an attachment disposition,
Content-Length, and the bundle’s fixed security headers. Symfony documents the
standard Response class and its header model
(https://symfony.com/doc/current/components/http_foundation.html).
Step 2 — Display a PDF inline
Section titled “Step 2 — Display a PDF inline”Use inline() when you want the browser to show the PDF instead of downloading
it:
return PdfResponse::inline($doc, 'preview.pdf');The disposition changes to inline. All other headers stay the same.
Step 3 — Stream a large PDF
Section titled “Step 3 — Stream a large PDF”Use the streamed variants for large documents. They emit the PDF in 64 KB
chunks, which reduces peak memory. They return a
Symfony\Component\HttpFoundation\StreamedResponse and omit Content-Length.
return PdfResponse::streamDownload($doc, 'annual-report.pdf');Use streamInline() for inline streaming. Symfony documents the
StreamedResponse callback contract, which is a void callable that flushes
output (https://symfony.com/doc/current/components/http_foundation.html).
Step 4 — Generate a PDF asynchronously
Section titled “Step 4 — Generate a PDF asynchronously”When symfony/messenger is installed, you can move generation out of the request
thread.
4a — Implement a builder
Section titled “4a — Implement a builder”Implement NextPDF\Symfony\Message\PdfBuilderInterface. The handler gives you a
fresh, pre-configured Document and the serializable context from the message.
<?php
declare(strict_types=1);
namespace App\Pdf;
use NextPDF\Core\Document;use NextPDF\Symfony\Message\PdfBuilderInterface;
final class InvoicePdfBuilder implements PdfBuilderInterface{ public function build(Document $document, array $context): Document { $document->addPage(); $document->setFont('dejavusans', '', 12); $document->cell(0, 10, 'Invoice #' . $context['invoice_id']);
return $document; }}4b — Register the builder in the locator
Section titled “4b — Register the builder in the locator”The handler resolves builders from a PHP Standard Recommendation 11 (PSR-11)
service locator keyed by class name. Only registered builders are reachable. Add
the builder to a locator in config/services.yaml:
services: App\Pdf\InvoicePdfBuilder: ~
nextpdf.pdf_builder_locator: class: Symfony\Component\DependencyInjection\ServiceLocator arguments: - 'App\Pdf\InvoicePdfBuilder': '@App\Pdf\InvoicePdfBuilder' tags: ['container.service_locator']
NextPDF\Symfony\Message\GeneratePdfHandler: arguments: $builderLocator: '@nextpdf.pdf_builder_locator'The handler asks the locator for the builder by its class-string id. In PSR-11, a container identifier is a string that uniquely identifies an entry (PSR-11 §1.1.2).
4c — Dispatch the message
Section titled “4c — Dispatch the message”Inject Symfony\Component\Messenger\MessageBusInterface, then dispatch the
message:
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Pdf\InvoicePdfBuilder;use NextPDF\Symfony\Message\GeneratePdfMessage;use Symfony\Component\HttpFoundation\Response;use Symfony\Component\Messenger\MessageBusInterface;use Symfony\Component\Routing\Attribute\Route;
final class ReportController{ #[Route('/invoice/{id}/queue', name: 'invoice_queue')] public function queue(MessageBusInterface $bus, int $id): Response { $bus->dispatch(new GeneratePdfMessage( builderClass: InvoicePdfBuilder::class, outputPath: '/var/storage/invoices/' . $id . '.pdf', builderContext: ['invoice_id' => $id], ));
return new Response('PDF generation queued.', 202); }}GeneratePdfMessage is a readonly data transfer object (DTO). Its constructor
rejects output paths that are empty, do not end in .pdf, include path-traversal
segments, use stream-wrapper schemes, or contain null bytes. It also requires
builderClass to be a syntactically valid class name. The handler validates the
output path again at execution time, before it writes. If a path is safe at
dispatch but unsafe at consume time, the handler still rejects it. The
#[AsMessageHandler] attribute and the MessageBusInterface dispatch contract
follow the standard Symfony Messenger model
(https://symfony.com/doc/current/messenger.html).
4d — Route the message and run a worker
Section titled “4d — Route the message and run a worker”In config/packages/messenger.yaml, route the message to a transport:
framework: messenger: transports: async: '%env(MESSENGER_TRANSPORT_DSN)%' routing: NextPDF\Symfony\Message\GeneratePdfMessage: asyncThen run a worker:
php bin/console messenger:consume asyncVerify it works
Section titled “Verify it works”php bin/console debug:container --tag=container.service_locatorphp bin/console messenger:consume async --limit=1 -vvThe first command confirms that the builder locator is registered. The second command consumes one queued message and prints the handler’s progress.
Next steps
Section titled “Next steps”- /integrations/symfony/configuration/ — adjust defaults, fonts, and the document service.
- /integrations/symfony/production-usage/ — review worker safety and streaming under load.
- /integrations/symfony/troubleshooting/ — resolve common boot and runtime issues.
Conformance
Section titled “Conformance”Each row is a normative claim made on this page and pinned to a full 64-hex
reference_id from the gated standards development organization (SDO) corpus.
_sidecars/rag-citations.yaml contains provenance, including the corpus
manifest and retrieval transport.
| Spec | Clause | reference_id | Claim |
|---|---|---|---|
| PSR-11 | psr_11_container#1.1.2.p4 | Container has()/get() identifier contract |
See also
Section titled “See also”- /integrations/symfony/overview/ — review the capability summary.
- /integrations/symfony/install/ — install and register the bundle.
- /integrations/symfony/integration/ — review the end-to-end wiring reference.