Browser API

usePDF

The usePDF composable owns a pdf() instance and exposes reactive render state for previewing in the browser. It is the Vue analog of react-pdf's usePDF hook and returns a [state, update] tuple.

Signature

ts
function usePDF(options?: {
  document?: DocumentTree
}): [Ref<PDFState>, (document: DocumentTree) => void]

Options

PropTypeDefaultDescription
documentDocumentTreeOptional initial document tree. Rendering starts immediately when provided; omit it and call the updater instead.

Return Value

A two-element tuple. The first element is a Ref holding the render state — read it as instance.value.url in script, or instance.url in a template. The second is an updater that swaps in a new document tree.

ts
const [instance, update] = usePDF()

State (instance.value)

PropTypeDefaultDescription
urlstring | nullnullBlob URL for preview. Revoked automatically when replaced or on unmount.
blobBlob | nullnullRaw binary of the generated PDF.
errorError | nullnullLast render error, if any.
loadingbooleanfalseWhether a render is currently in flight. Starts as true when an initial document is passed.

Updater

PropTypeDefaultDescription
update(document)(document: DocumentTree) => voidReplaces the rendered document. Renders run through a concurrency-1 queue where a newly scheduled render replaces any pending one.

Usage

usePDF takes a document tree — a plain object of { type, props, children } nodes — not a Vue component:

vue
<script setup lang="ts">
import { usePDF } from '@vuepdf/renderer'

const [instance] = usePDF({
  document: {
    type: 'DOCUMENT',
    props: { title: 'Hello' },
    children: [
      {
        type: 'PAGE',
        props: { size: 'A4' },
        children: [
          { type: 'TEXT', props: {}, children: ['Hello, PDF!'] },
        ],
      },
    ],
  },
})
</script>

<template>
  <div v-if="instance.loading" class="loading">Generating PDF...</div>
  <iframe
    v-else-if="instance.url"
    :src="instance.url"
    width="100%"
    height="500px"
    class="pdf-preview"
  />
</template>

Updating on Data Changes

Call the updater whenever your data changes to re-render:

vue
<script setup lang="ts">
import { usePDF } from '@vuepdf/renderer'

const [instance, update] = usePDF()

const buildTree = (invoice: { id: string; amount: number }) => ({
  type: 'DOCUMENT',
  props: {},
  children: [
    {
      type: 'PAGE',
      props: { size: 'A4' },
      children: [
        {
          type: 'TEXT',
          props: {},
          children: [`Invoice ${invoice.id} — $${invoice.amount}`],
        },
      ],
    },
  ],
})

const invoice = ref({ id: '123', amount: 1500 })

// Re-render whenever the data changes; pending renders are replaced.
watchEffect(() => update(buildTree(invoice.value)))
</script>

Prefer a Component Wrapper

Hand-writing trees is rarely what you want. PDFViewer, PDFDownloadLink, and BlobProvider each run usePDF internally and accept a slotted <Document>, so you can compose your PDF with the normal components. PDFViewer takes it in the default slot; the other two take it in a #document slot and keep the default slot for your own markup:

vue
<script setup lang="ts">
import { PDFViewer } from '@vuepdf/renderer'
import InvoiceDoc from './InvoiceDoc.vue'
</script>

<template>
  <PDFViewer :style="{ width: '100%', height: '500px' }">
    <InvoiceDoc :invoice="invoice" />
  </PDFViewer>
</template>
Reach for usePDF directly only when you need to own the render loop — for example, driving a preview from a tree you build programmatically.
usePDF is browser-only. On the server, use the pdf() function or the renderTo* APIs instead.