@lupinum/nuxt-pdf
Author and render PDFs with Vue components in Nuxt.
[!IMPORTANT] Nuxt PDF is in public beta. Its tested behavior is stable within each release, but a minor release can contain documented breaking changes before version 1.0.
Why use Nuxt PDF?
Nuxt PDF discovers Vue Single File Components in pdfs/. It renders these
components on the Node server through a PDF layout engine. The client bundle
does not contain the renderer or document templates.
This approach gives you typed document props, reusable Vue components, stable server output, and PDF-specific test helpers in one Nuxt module.
When to use it
Use Nuxt PDF for structured documents such as invoices, reports, certificates, tickets, and labels.
Do not use Nuxt PDF to print an existing web page, display an existing PDF, or edit and sign an existing PDF.
Requirements
- Node.js
^22.14.0,^24.0.0, or^26.0.0 - Nuxt
^4.4.8 - Vue
^3.5.0
Nuxt 3, Node 20, browser rendering, and edge rendering are outside the current support boundary.
Installation
Install the module in a Nuxt 4 application:
pnpm add @lupinum/nuxt-pdf
Add the module to nuxt.config.ts:
export default defineNuxtConfig({
modules: ['@lupinum/nuxt-pdf'],
})
Quick start
Create pdfs/invoice.vue:
<script setup lang="ts">
type InvoiceProps = {
invoice: {
customer: string
number: string
total: string
}
}
const props = defineProps<InvoiceProps>()
definePdf<InvoiceProps>({
title: ({ invoice }) => `Invoice ${invoice.number}`,
filename: ({ invoice }) => `invoice-${invoice.number}.pdf`,
language: 'en-GB',
sampleData: {
invoice: {
customer: 'Ada Lovelace',
number: 'INV-001',
total: 'EUR 1,250.00',
},
},
})
</script>
<template>
<PdfDocument>
<PdfPage size="A4" :style="{ fontSize: 11, padding: 48 }">
<PdfText :style="{ fontSize: 24, marginBottom: 24 }">
Invoice {{ props.invoice.number }}
</PdfText>
<PdfText>{{ props.invoice.customer }}</PdfText>
<PdfText :style="{ marginTop: 12 }">
Total: {{ props.invoice.total }}
</PdfText>
</PdfPage>
</PdfDocument>
</template>
Create server/api/invoice.get.ts:
import { pdf } from '#pdf'
export default defineEventHandler(async () => {
const result = await pdf.invoice.render({
invoice: {
customer: 'Ada Lovelace',
number: 'INV-001',
total: 'EUR 1,250.00',
},
})
return result.response()
})
Start Nuxt:
pnpm dev
Download the PDF from a second terminal:
curl -o invoice.pdf http://localhost:3000/api/invoice
Open these development routes:
/_pdflists the discovered templates./_pdf/invoiceshows the document preview./api/invoicereturns the production-style PDF response.
Nuxt PDF regenerates the typed #pdf registry whenever templates change. If
your editor still shows #pdf as untyped, run nuxt prepare once to refresh
the generated types.
Agent setup
@lupinum/nuxt-pdf ships its documentation inside the package for coding
agents such as Claude Code, Codex and Cursor. The pages match the installed
version, so an agent does not follow the website for a different version.
To set it up, give your coding agent this task:
Set up @lupinum/nuxt-pdf for coding agents as described under "Agent setup" in its README.
Steps for the agent
-
Find
node_modules/@lupinum/nuxt-pdf/dist/agent/AGENTS.md. Look in the directory of the project that depends on@lupinum/nuxt-pdffirst, then in its parent directories up to the repository root (workspaces can hoist packages). Read it; it lists the documentation pages. -
Add the section below to the project's agent instructions:
AGENTS.md, orCLAUDE.mdif the project has only that file. If it has neither, createAGENTS.md. Write the path relative to the repository root, throughnode_modules/@lupinum/nuxt-pdf(for exampleapps/web/node_modules/@lupinum/nuxt-pdf/...in a workspace). Never write a resolved path such asnode_modules/.pnpm/...: it contains the version and breaks after an upgrade. If a section for@lupinum/nuxt-pdfalready exists, leave it as it is.Use the path you found in place of the sample path:
## @lupinum/nuxt-pdf Before you change code that uses @lupinum/nuxt-pdf, read `node_modules/@lupinum/nuxt-pdf/dist/agent/AGENTS.md` and the pages it lists. They document the installed version. Prefer them over what you remember about this package and over the website. -
Do not copy the documentation into the project and do not install a skill. The section points into the installed package, so it stays correct after every upgrade or downgrade.
If the file does not exist, the installed version has no packaged documentation. Read the package README and its TypeScript types instead.
Discord
Join the Lupinum OSS community to discuss Nuxt PDF, ask questions, and share what you build.
How it works
PDF templates use typed props, interpolation, v-if, keyed v-for, slots,
and local Vue components. Nuxt PDF provides document, page, text, image, link,
note, and SVG primitives.
Templates run in an isolated Vue application on the Node server. They do not inherit Nuxt plugins, app-level provides, browser globals, or DOM components. Load request data before the render and pass it through typed props.
Nuxt PDF supports local images and fonts. Remote images are disabled until an operator configures an explicit HTTPS allowlist.
Test a document
Install the optional test dependencies:
pnpm add -D pdfjs-dist @napi-rs/canvas
Render the real template in a test:
import { expectPdf, renderPdfSfc } from '@lupinum/nuxt-pdf/test'
const { parsed } = await renderPdfSfc('./pdfs/invoice.vue', {
invoice: {
customer: 'Ada Lovelace',
number: 'INV-001',
total: 'EUR 1,250.00',
},
})
expectPdf(parsed)
.toHavePageCount(1)
.toContainText('Invoice INV-001')
The test entry can also inspect links, bookmarks, page geometry, and raster baselines.
Documentation
Read the Nuxt PDF documentation for authoring, assets, links, bookmarks, tables of contents, testing, and the complete API reference. Read CONFORMANCE.md for the exact tested behavior and known limitations.
Coding agents should start with
/llms.txt. It links to concise raw
Markdown for common tasks, reference pages, errors, and compatibility evidence.
Contributing and development
pnpm install --frozen-lockfile
pnpm dev
pnpm verify
Use pnpm docs:dev to run the documentation site. pnpm verify runs the same
checks as CI.
Read CONTRIBUTING.md before you open a pull request.
Support and security
- Read the documentation.
- Join the Lupinum OSS Discord.
- Open a bug report for a reproducible defect.
- Open a feature request before you plan a larger change.
- Follow SECURITY.md for a private vulnerability report.
License
MIT © Lupinum OG and contributors.
Third-party attributions are in THIRD_PARTY_NOTICES.md.
Related Packages
One utility function for module authors to define compiler constants for all bui...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.3.1 | ^3.5.0 | MIT | |
| 0.3.0 | ^3.5.0 | MIT |
Pre-releases (6)
| Version | Released | Vue | License |
|---|---|---|---|
| 0.4.0-beta.6 | ^3.5.0 | MIT | |
| 0.4.0-beta.5 | ^3.5.0 | MIT | |
| 0.4.0-beta.4 | ^3.5.0 | MIT | |
| 0.4.0-beta.3 | ^3.5.0 | MIT | |
| 0.4.0-beta.2 | ^3.5.0 | MIT | |
| 0.4.0-beta.1 | ^3.5.0 | MIT |