padocia/laravel-nova-pdf
Laravel Nova PDF adds PDF handling to Nova resources, letting you generate, preview, and download PDF documents from your admin panel using familiar Laravel and Blade workflows.
Whether to fund document output at all — and how to scope it. This package is a concrete data point in the "should the back office export records as PDFs?" conversation. It demonstrates that single-record PDF generation is a small capability (~1–3 engineering days for a spike), which reframes the roadmap question from "do we need a document pipeline?" to "do we wire one Nova action to one Blade template?" That's a useful forcing function, and it's an honest argument against over-scoping invoices, receipts, or statements as a multi-sprint epic before anyone has tried the cheap version.
Build vs. buy vs. harvest. The durable decision is about the pattern — Nova Action → Blade view → server-side HTML→PDF → download — not about this specific package. The source is small enough to read end to end in an afternoon; the package is a thin wrapper around Browsershot::html(). So the PM framing is "own ~150 lines behind our own PdfRenderer interface" versus "take a dependency." Given the package's maturity, the answer is unusually clear in favor of owning the seam.
Runtime commitment — the decision people miss. The handleBrowsershotOptions fix in v1.0.2 reveals the rendering engine is Browsershot: headless Chromium via Node/Puppeteer. Adopting this means adopting a Node runtime, npm, and a pinned, patched Chrome binary as a side effect of an export button. That belongs on the roadmap as an infrastructure line item, not a footnote on a feature ticket. If we already run headless Chrome for screenshots or reporting, marginal cost collapses and this is cheap. If we don't, the "free MIT package" is actually a new sidecar to operate and secure.
Internal vs. customer-facing boundary. This is an admin-panel convenience. It should never be cited as the foundation for emailed invoices, customer portals, or signed documents — those require template ownership, retries, storage retention, and auditability that no 2021 Nova add-on supplies. Drawing that line early prevents a support-workflow feature from quietly acquiring contractual obligations.
Sequencing and scope discipline. The synchronous, single-record, HTML/CSS-template design narrows the design space early. That's correct for ops printing a record off the detail view, and wrong for batch billing or nightly statement runs. It forces the "who generates documents, how many per day, and must they exist later?" conversation now instead of at launch. Related decisions it informs: whether we need a private disk plus signed temporary URLs (yes, if anything persists), whether renders get queued (yes, if they exceed a couple of seconds), and whether authorization sits in the action's handle() (it must — an export is an egress path and should be treated like one).
Dependency hygiene in internal tooling. With 23 stars, zero dependents, and a four-year gap in releases followed by a sudden 1.0.x revival line, this belongs on the "reference only, do not standardize" list. It informs a decision to avoid dependency sprawl in admin tooling as much as one to adopt anything. The v1.0.2 fix is itself a signal: a real config-plumbing bug reached a tagged release, and the option path remains the least-tested surface.
Consider it only when all of the following hold:
spatie/browsershot, pin the Chromium image version, and write a one-paragraph ADR documenting the replacement plan while the code is small. At 23 stars and zero dependents, you are the QA: composer audit and a Chrome/Puppeteer CVE scan go into the install gate.handle(), private disk storage, signed/temporary URLs with an expiry matched to retention, and a render worker with scoped network access (no metadata endpoints, no cluster-internal DNS). If you cannot commit to these, do not ship the action on the index table where "select all, export" becomes a data-loss story.Look elsewhere — or write it yourself — when:
waitUntil semantics shifting between Browsershot versions mean the output is only as stable as your container image. If pixel-exact output is a requirement, that's an owned pipeline, not an add-on.1.0.x revival line is not a maintenance guarantee. Treat it as a reference implementation you can read, learn from, and reimplement.To executives (one paragraph): We have a recurring request to let the back office export records as PDFs, and the instinct is to scope it as a document pipeline. This package shows the cheap version instead: one Nova action, one Blade template, one headless-Chromium render, one download. That's roughly one to three engineering days to prove, and it lets us ship a genuinely useful admin convenience without committing to a multi-sprint platform. The catch is honest and worth saying out loud: the "free MIT package" quietly requires a Node/Chromium runtime in production, and it's a 2021 wrapper with 23 stars and zero dependents — so we'd own it, not depend on it. My recommendation is a short spike, then own the ~150 lines ourselves behind a PdfRenderer interface. That gives us the capability with a dependency we control, and it keeps us well clear of implying this is the foundation for customer-facing invoices or contracts.
To engineering (the ask): Treat this as a reference implementation, not an adoption. Spike on a branch: install, publish config and views, wire one action to one record, render a page with known measurements, and confirm the option path (format, margins, waitUntil, timeout, nodeBinary, chromePath) actually behaves — v1.0.2 is literally a fix for that plumbing, so do not trust the config file. Verify Nova version compatibility before anything else. Then plan to reimplement behind our own interface: a PdfRenderer returning raw bytes, one Blade template per document type (not per resource), a plain view-model array rather than the Eloquent model, explicit inline-vs-attachment, queueing the moment renders exceed a second or two, private disk plus temporary URLs, and authorize('view', ...) inside handle(). Pin the package, pin Browsershot, pin the Chromium image, and document the replace plan while the code is still small.
The one-line version for a steering update: Small capability, cheap to prove, non-trivial to operate — worth a one-week spike to own it ourselves, not worth standardizing on the package.
How can I help you explore Laravel packages today?