@jasy/nuxt

@jasy/nuxt
Author PDFs as Vue components in Nuxt - render them in the browser or on a server route, zero config.
The Nuxt module for @jasy/vue and @jasy/pdf.
Add it to modules and the components and render helpers are just there - no imports, no wiring. Render a
PDF client-side with usePdf, or stream one from a Nitro route with definePdfHandler. No headless
browser, no Java.
Install
npx nuxi module add @jasy/nuxt
That installs the package and adds it to modules for you. Or wire it up by hand:
pnpm add @jasy/nuxt
// nuxt.config.ts
export default defineNuxtConfig({
modules: ["@jasy/nuxt"],
});
Client: usePdf
Author a PDF as a Vue component - the jasy components are auto-registered, so no imports:
<!-- Invoice.vue -->
<template>
<Document :size="11">
<Page :size="'A4'" :margin="48">
<Text :size="24" bold color="#0a2348">Invoice #{{ id }}</Text>
</Page>
</Document>
</template>
<script setup lang="ts">
import Invoice from "./Invoice.vue";
const { open, download, pending } = usePdf(Invoice, { props: { id: 42 } });
</script>
<template>
<button :disabled="pending" @click="open">View PDF</button>
</template>
open() and download() render on demand and reuse the result, so one click is one render. Pass
{ immediate: true } to pre-render on mount.
Server: definePdfHandler
The @jasy/pdf tree API is auto-imported in server/. No Vue, no browser - build the document and stream
it:
// server/api/invoice/[id].get.ts
export default definePdfHandler((event) =>
Document([
Page({ size: "A4", margin: 48 }, [
Text(`Invoice #${getRouterParam(event, "id")}`, { size: 24, bold: true }),
]),
]),
);
GET /api/invoice/42 streams application/pdf. Need auth or a data fetch first? Use
sendPdf(event, doc, opts) inside your own handler, or renderToBytes(doc) to just get the bytes (save
them, attach to an email, anything).
Caching
Wrap a route in Nitro's cache - keyed by path + query, so it caches per request out of the box:
export default definePdfHandler(build, { cache: { maxAge: 3600 } });
Expired entries re-render fresh (swr is off by default) - a stale invoice is never served.
Password protection
usePdf and definePdfHandler / sendPdf forward @jasy/pdf render options via renderOptions - so
AES-256 password protection is one option away, client or server:
// client
const { download } = usePdf(Invoice, { renderOptions: { encrypt: { userPassword: "secret" } } });
// server
export default definePdfHandler(build, { renderOptions: { encrypt: { userPassword: "secret" } } });
ownerPassword + permissions are optional. (AES-256 ≠ PDF/A, so this is not for ZUGFeRD invoices.)
What the module sets up
- Components for templates:
Document·Page·Column·Row·Box·Padding·Text·Image·Tableand the rest. Set aprefixto dodge name clashes (prefix: "Pdf"makes it<PdfDocument>). - Auto-imports:
usePdfandrenderToPdfon the client; the@jasy/pdftree API,definePdfHandlerandsendPdfon the server. - Bundle-safety: keeps jimp (the server-side image decoder) out of the client bundle - the browser decodes images via canvas instead.
Options
export default defineNuxtConfig({
modules: ["@jasy/nuxt"],
jasy: {
autoImport: true, // auto-register components + the tree API (default true)
prefix: "Pdf", // <PdfDocument> in templates, PdfDocument(...) in server/ (default none)
},
});
Links
@jasy/pdf- the pure-TypeScript PDF engine@jasy/vue- author PDFs as Vue components- jasy.dev - documentation
MIT, by Florian Heuberger