institut-agro-ead/html2media
A Laravel/PHP package for converting HTML into media such as images or PDFs. Generate visual assets from Blade views or raw markup via a simple, configurable API—useful for reports, thumbnails, social cards, and automated document generation.
html2media is a thin Laravel wrapper around HTML → media conversion (PDF and images), with a fluent call-site API and a config file for output defaults. The day-to-day value is that you stop shelling out to headless Chrome or wkhtmltopdf and express “turn this Blade view into a PDF” in framework idioms.
Minimal steps:
Install and publish config:
composer require institut-agro-ead/html2media
php artisan vendor:publish --tag=html2media-config
Read config/html2media.php first — this is where file name, orientation, margins, paper size, output format/quality, and (critically, per v1.0.3) the rerender/print behaviour live. Most surprises are config keys, not code.
Check the service provider for the binding and any facade alias, so you know whether to inject a class or call a facade.
First use case — an invoice PDF from a Blade view:
use InstitutAgroEad\Html2Media\Facades\Html2Media;
Route::get('/invoices/{invoice}/pdf', function (Invoice $invoice) {
return Html2Media::view('invoices.pdf', ['invoice' => $invoice])
->filename("invoice-{$invoice->number}.pdf")
->orientation('portrait')
->download();
});
If that returns a PDF, you have the whole core loop. If you use Filament, register/see the action integration next — that's the second surface area worth wiring early.
Blade view as the primary source of truth. Treat the PDF template as a normal Blade view under resources/views/pdf/ and pass data in like any other view. This keeps print layouts reviewable, diffable, and testable with the same tooling as web views. Reserve raw-HTML strings for content that is already rendered elsewhere (rich-text fields, generated tables).
Three delivery modes, one builder. The package distinguishes download vs print (and image export). Model this explicitly in your controller:
$doc = Html2Media::view('invoices.pdf', compact('invoice'));
return match ($request->query('mode')) {
'print' => $doc->print(), // browser print / inline
'preview' => $doc->image(), // thumbnail
default => $doc->download(), // attachment
};
Per-document overrides, not global config. Keep config/html2media.php as the safe default and set orientation, margins, filename, and format at the call site. Different documents legitimately want different layouts; encoding that in config turns into constant churn.
Filament action pattern. For admin panels, wrap generation in a Filament action whose closure builds the payload at click time (not at mount time) so you render current row state:
Action::make('downloadPdf')
->action(fn (Invoice $record) => Html2Media::view('invoices.pdf', ['invoice' => $record])
->filename("invoice-{$record->number}.pdf")
->download());
Don't precompute a closure over stale Eloquent state; pass the record and let the action resolve it.
Queue heavy documents. Large templates (many line items, embedded images) are CPU/memory heavy. Dispatch a job, write the file to a disk (Storage::disk('invoices')->put(...)), then notify or expose a signed URL. This also dodges request timeouts.
Centralize behind an adapter. v1.0.x is still stabilising (the 1.0.3 patch fixes rerender behaviour). Wrap Html2Media in a small DocumentRenderer service so an upgrade or API shift touches one class, not every controller and Filament resource.
Testing. Assert on the artifact, not the implementation: response content type, non-empty binary, and a snapshot of the HTML passed to the converter. A cheap smoke test that renders your two highest-value documents will catch silent regressions where a template still “works” but produces a blank or truncated PDF.
dd() inside the path to confirm the forced branch executed — the visible output looks the same either way.php artisan config:cache in production means edits to html2media.php (orientation, margins, rerender behaviour) won‘t take effect until you re-run it. If toggling a setting changes nothing, this is almost always why.php artisan view:clear to distinguish template compilation from package caching.asset() paths require a correct APP_URL in the environment doing the rendering (queues included). For logos and fonts, prefer absolute URLs or inlined base64 — a headless renderer on a worker has no browser context and will silently drop unresolvable resources.Cache::lock(...).memory_limit and PHP execution time rather than failing gracefully. Push to a queue and set an explicit timeout on the job before you discover this in production."institut-agro-ead/html2media": "1.0.*") and keep the adapter thin. The rerender fix signals active maintenance and ongoing churn — budget for a minor upgrade when it lands.How can I help you explore Laravel packages today?