Nuxt integration
Use the same document components in a client preview or a server endpoint. Keep browser-only rendering behind a client boundary.
Configure the Vue compiler
Forme components emit custom document elements. Tell the compiler that forme-* tags are custom elements.
nuxt.config.ts
export default defineNuxtConfig({
vue: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('forme-'),
},
},
})Create a client preview
Wrap the preview in ClientOnly or put it in a .client.vue component. Import the browser renderer inside a client lifecycle hook so WASM initialization never runs during SSR. The rendering guide shows how to create the PDF bytes.
components/PdfPreview.client.vue
<script setup lang="ts">
import InvoiceMinimal from '@/components/pdf/blocks/invoice-minimal/InvoiceMinimal.vue'
import { onBeforeUnmount, onMounted, ref } from 'vue'
const pdfUrl = ref<string | null>(null)
const error = ref<string | null>(null)
let disposed = false
onMounted(async () => {
try {
const [{ serialize }, { init, renderSerializedDoc }, { default: wasmUrl }]
= await Promise.all([
import('@formepdf/vue'),
import('@formepdf/core/worker'),
import('@formepdf/core/pkg-web/forme_bg.wasm?url'),
])
await init(wasmUrl)
const document = await serialize(InvoiceMinimal)
const bytes = await renderSerializedDoc(document)
if (disposed) return
pdfUrl.value = URL.createObjectURL(new Blob([new Uint8Array(bytes)], { type: 'application/pdf' }))
} catch {
if (!disposed) error.value = 'The PDF could not be rendered.'
}
})
onBeforeUnmount(() => {
disposed = true
if (pdfUrl.value) URL.revokeObjectURL(pdfUrl.value)
})
</script>
<template>
<p v-if="error" role="alert">{{ error }}</p>
<a v-else-if="pdfUrl" :href="pdfUrl" download="invoice.pdf">Download invoice</a>
<p v-else role="status">Rendering PDF…</p>
</template>Return a PDF from an API route
This example targets a Node deployment with the Forme renderer installed. Edge runtimes need their own WASM setup. Validate incoming invoice data at the request boundary before passing it to your document.
server/api/invoice.get.ts
import { renderSerializedDoc } from '@formepdf/core'
import { serialize } from '@formepdf/vue'
import InvoiceMinimal from '../../app/components/pdf/blocks/invoice-minimal/InvoiceMinimal.vue'
export default defineEventHandler(async (event) => {
const document = await serialize(InvoiceMinimal)
const bytes = await renderSerializedDoc(document)
setHeader(event, 'Content-Type', 'application/pdf')
setHeader(event, 'Content-Disposition', 'attachment; filename="invoice.pdf"')
return bytes
})Keep themes separate from site styles
Nuxt color mode controls your website. A PdfcnTheme controls your PDF. Export a theme from the theme builder and pass it to a block or PdfcnThemeProvider.