Skip to main content
@formepdf/vue is the Vue adapter for Forme. It ships the same components with the same props as @formepdf/react, authored as ordinary .vue single-file components, and serializes to the identical document model. v-for, v-if, slots, and {{ }} interpolation just work — templates are plain Vue 3 components rendered on the server, with no special template language. There are two doors into Forme from a Vue app:
  1. Author documents as components.vue files using the Forme component set. This page. Full layout control, typed props, structural parity with the React and Svelte adapters.
  2. Render existing HTML — you already have an HTML/CSS template (from a CMS, an email system, a designer) and just want PDF bytes. Reach for @formepdf/html instead; it takes an HTML string and returns a PDF, naming anything outside the supported subset in a warnings[] array rather than dropping it silently.
Pick door 1 when you control the template and want the engine’s full component vocabulary; pick door 2 when you’re replacing a Puppeteer pipeline over HTML you don’t want to rewrite.

Install

The adapter requires Vue 3 (^3.4.0) as a peer dependency. @formepdf/core is an optional peer: it is only needed to render PDF bytes locally (renderDocument). If you serialize templates and POST the JSON to the hosted API, skip it — serialize works with zero WASM. Enable the custom-element compiler option so Vue leaves Forme’s internal placeholder tags alone. In vite.config.ts:

Quickstart

Create a template as a normal single-file component, src/components/Invoice.vue:
Serve it as a PDF from a Nitro (Nuxt) endpoint, server/routes/invoice.get.ts:
That is the whole route. renderDocument serializes the template and renders it through the WASM engine in one call. Using the hosted API instead? Serialize without rendering — no @formepdf/core install needed:
Vue components cannot be pre-bound to props, so the adapter’s serialize, render, and renderDocument take the component plus a props option (renderDocument(Invoice, { props })) — the same shape as the Svelte adapter. All three are async: renderToString from vue/server-renderer returns a promise.

Component parity

The adapter is 1:1 with @formepdf/react: the same components with the same props.
  • Layout: Document, Page, View, Text, Image, Fixed, PageBreak
  • Semantics: H1-H6, OrderedList, UnorderedList, ListItem, Strong, Em, Code, Link
  • Tables: Table, Row, Cell
  • Graphics: Svg, QrCode, Barcode, Canvas, Watermark
  • Charts: BarChart, LineChart, PieChart, AreaChart, DotPlot
  • Form fields: TextField, Checkbox, Dropdown, RadioButton
Everything in the components reference and styles reference applies verbatim — document-level props (metadata, lang, pdfUa, pdfa, certification, fonts), CSS string shorthands (border: "1px solid #000", padding: "8 16"), StyleSheet.create(), and the Style type are identical. Only the syntax around the components changes. The equivalence is enforced in CI: a catalog document authored in Vue and in React must serialize to the same document model. Nested <Text> spans become styled text runs, so mixed-style lines work exactly as in react:
Compiled templates (forme build --template, the $ref/$each/$if expression system for rendering without a JavaScript runtime) are TSX-only today. Vue templates always serialize by rendering the component server-side; to use stored templates with the hosted API, author them in TSX.

Page numbers

The engine substitutes the placeholders {{pageNumber}} and {{totalPages}} in text at render time. In a Vue template you cannot type {{pageNumber}} directly — Vue parses double braces as an interpolation expression and would look for a pageNumber variable. Interpolate the exported PAGE_NUMBER and TOTAL_PAGES constants instead (their values are the placeholder strings):

Fonts

Font.register() has the same API as react and feeds the same process-wide store. Register in a plain <script> block (not <script setup>) so registration runs once at module evaluation, not on every render:
Per-document registration via <Document :fonts="[...]"> works too. See the fonts guide for sources, weights, and fallback chains.

Tailwind

@formepdf/tailwind’s tw() works unchanged — it returns a plain style object:

Custom graphics

<Canvas> takes a draw callback that records vector operations:
The draw callback runs during server-side serialization, not at PDF render time. It must be synchronous and pure: no await, no side effects, no browser or runtime APIs.

Render options

renderDocument and renderDocumentWithLayout forward all @formepdf/core render options unchanged, so features like embedded data and form flattening work exactly as documented for react:

Migrating from TSX

Migration is mechanical: component names, props, and style objects are identical, so most of a template moves over as-is. The react quickstart maps to Vue like this: Three differences to note. First, react’s renderDocument takes an element (<Document>...), while Vue components cannot be pre-bound to props — so the Vue adapter’s renderDocument, serialize, and render take the component plus a props option, and are async. Second, always bind object props (:style="{...}", not style="{...}") so Vue passes the object through instead of coercing it to a string attribute. Third, whitespace in .vue templates is normalized to the same rules JSX uses, so indentation never leaks stray spaces into rendered text.