Node API

renderToBuffer

Renders a PDF document and resolves with a Node Buffer. Use this when you need the bytes in memory — to upload them, attach them to an email, or hand them to another library.

Signature

ts
function renderToBuffer(
  element: Component,
  props?: Record<string, unknown>
): Promise<Buffer>

Usage

ts
import { renderToBuffer } from '@vuepdf/renderer'
import MyDocument from './MyDocument.vue'

const buffer = await renderToBuffer(MyDocument)

// Write it, upload it, or attach it
await fs.promises.writeFile('out.pdf', buffer)

Attaching to an Email

ts
import { renderToBuffer } from '@vuepdf/renderer'
import Invoice from './Invoice.vue'

const buffer = await renderToBuffer(Invoice)

await transporter.sendMail({
  to: 'customer@example.com',
  subject: 'Your invoice',
  attachments: [{ filename: 'invoice.pdf', content: buffer }],
})

Nuxt Server Route

Returning a Buffer from an event handler is enough — Nitro sends it as-is. Because you hold the whole document in memory you can set content-length and force a download:

server/api/invoice/[id].get.ts
// server/api/invoice/[id].get.ts
import { renderToBuffer } from '@vuepdf/renderer'
import InvoiceDocument from '../../../components/pdf/InvoiceDocument.vue'

export default defineEventHandler(async (event) => {
  const id = getRouterParam(event, 'id')!
  const record = await useDb().findInvoice(id)

  if (!record) {
    throw createError({ statusCode: 404, statusMessage: 'Invoice not found' })
  }

  const buffer = await renderToBuffer(InvoiceDocument, {
    id,
    total: record.total,
  })

  setHeader(event, 'content-type', 'application/pdf')
  setHeader(event, 'content-length', buffer.length)
  setHeader(event, 'content-disposition', `attachment; filename="invoice-${id}.pdf"`)

  return buffer
})
components/pdf/InvoiceDocument.vue
<!-- components/pdf/InvoiceDocument.vue -->
<script setup lang="ts">
import { Document, Page, Text } from '@vuepdf/renderer/components'

const props = defineProps<{
  id: string
  total: string
}>()
</script>

<template>
  <Document :title="`Invoice ${props.id}`">
    <Page size="A4" :style="{ padding: 48, fontSize: 12 }">
      <Text :style="{ fontSize: 22, marginBottom: 16 }">
        Invoice {{ props.id }}
      </Text>
      <Text>
        Total due: {{ props.total }}
      </Text>
    </Page>
  </Document>
</template>
Keep the PDF primitive imports in the document SFC, not the server route. In server routes, import the document component and pass props as the second argument to renderToBuffer. Importing a .vue document from server/ requires the @vuepdf/nuxt module — see renderToStream for why.

Caching the Result

Rendering is CPU-bound. If the document changes rarely, wrap it in defineCachedFunction so repeat requests serve stored bytes:

server/api/report.get.ts
// server/api/report.get.ts
import { renderToBuffer } from '@vuepdf/renderer'
import ReportDocument from '../../components/pdf/ReportDocument.vue'

// Nitro's cache stores JSON, which does not survive a Buffer — keep base64
// in the cache and rehydrate on the way out.
const buildPdf = defineCachedFunction(
  async () => (await renderToBuffer(ReportDocument)).toString('base64'),
  { maxAge: 60 * 60, name: 'report-pdf', getKey: () => 'latest' },
)

export default defineEventHandler(async (event) => {
  const buffer = Buffer.from(await buildPdf(), 'base64')

  setHeader(event, 'content-type', 'application/pdf')
  setHeader(event, 'content-length', buffer.length)

  return buffer
})
Do not cache the Buffer itself. Nitro's cache serializes to JSON, so a cached Buffer comes back as { type: 'Buffer', data: [...] } — the first request succeeds and every cache hit afterwards fails with ERR_HTTP_INVALID_HEADER_VALUE on content-length. Store base64 and rehydrate, as above.

Choosing Between the Node APIs

PropTypeDefaultDescription
renderToBufferPromise<Buffer>You need the bytes in memory — uploads, email attachments, further processing.
renderToStreamPromise<ReadableStream>You are piping to an HTTP response or a file and want to avoid buffering the whole document.
renderToFilePromise<void>You just want the PDF written to a path on disk.
This is a Node-only API. Calling it in a browser build throws — use pdf() and toBlob() there instead.