@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:
- Author documents as components —
.vuefiles using the Forme component set. This page. Full layout control, typed props, structural parity with the React and Svelte adapters. - 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/htmlinstead; it takes an HTML string and returns a PDF, naming anything outside the supported subset in awarnings[]array rather than dropping it silently.
Install
^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:
server/routes/invoice.get.ts:
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
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:
<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.