Forms
vue-pdf can produce interactive PDF forms — documents with fields a reader can fill in, tick, and save. Fields are backed by the PDF AcroForm specification, so they work in Acrobat, Preview, Chrome's built-in viewer, and most other readers.
There is nothing to enable. The moment you render your first form component, vue-pdf initialises the document's AcroForm for you.
Components
Five components make up the forms API:
- TextInput — free text, single or multiline
- Checkbox — a two-state toggle
- Select — a dropdown of predefined options
- List — a scrollable list of predefined options
- FieldSet — an invisible grouping wrapper
Your First Field
Every field needs a name — that is the key the value is stored under when the form is filled in and exported. Fields are laid out with flexbox like any other element, so give them a size through style:
<template>
<Document>
<Page size="A4" :style="{ padding: 40 }">
<Text :style="{ fontSize: 10, marginBottom: 4 }">Full name</Text>
<TextInput name="fullName" :style="{ height: 24 }" />
</Page>
</Document>
</template>height (and a width, if it is not stretching to fill its parent) collapses to nothing and will be invisible in the reader. Common Props
Every form component except <FieldSet> accepts this shared set of props:
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | '' | Field identifier. This is the key the value is exported under. |
value | string | number | — | The field's current value. |
defaultValue | string | number | — | The value the field reverts to when the form is reset. |
required | boolean | false | Marks the field as mandatory before the form can be submitted. |
readOnly | boolean | false | Displays the value but prevents the reader from editing it. |
noExport | boolean | false | Excludes the field from the exported form data. |
<Checkbox checked /> is the same as <Checkbox :checked="true" />. Multi-word props accept either casing: read-only and readOnly are equivalent. Text Fields
<template>
<Document>
<Page size="A4" :style="{ padding: 40 }">
<!-- Single line -->
<TextInput name="email" value="me@example.com" :style="{ height: 24 }" />
<!-- Multiline, with a character limit -->
<TextInput
name="notes"
multiline
:max-length="500"
:style="{ height: 90, fontSize: 9 }"
/>
<!-- Masked -->
<TextInput name="pin" password :style="{ height: 24 }" />
</Page>
</Document>
</template> Leave fontSize off (or set it to 0) to let the reader auto-size text to the field. See the TextInput reference for the full prop list.
Checkboxes
<template>
<Document>
<Page size="A4" :style="{ padding: 40 }">
<!-- Unchecked -->
<Checkbox name="subscribe" :style="{ width: 14, height: 14 }" />
<!-- Checked, rendered as an X instead of a tick -->
<Checkbox
name="agree"
checked
x-mark
:style="{ width: 14, height: 14 }"
/>
</Page>
</Document>
</template> A checkbox stores one of two string states — onState (default Yes) and offState (default Off). Override them when your downstream consumer expects specific values, and keep them consistent across every checkbox in a document: some readers behave unpredictably when checkboxes in the same form use different state names.
Selects and Lists
<Select> renders a dropdown, <List> a scrollable box. They take the same props — the only difference is how the reader draws them.
<template>
<Document>
<Page size="A4" :style="{ padding: 40 }">
<!-- Dropdown -->
<Select
name="country"
:select="['Nigeria', 'Ghana', 'Kenya']"
:style="{ height: 20 }"
/>
<!-- Scrollable list, multiple selection -->
<List
name="languages"
multi-select
:select="['English', 'French', 'Yoruba', 'Hausa']"
:style="{ height: 60 }"
/>
</Page>
</Document>
</template>Grouping With FieldSet
<FieldSet> draws nothing. It exists to namespace the fields inside it, which lets you reuse the same field names in different sections and get a structured object out when the form is read back:
<template>
<Document>
<Page size="A4" :style="{ padding: 40 }">
<FieldSet name="billing">
<TextInput name="street" :style="{ height: 24 }" />
<TextInput name="city" :style="{ height: 24 }" />
</FieldSet>
<FieldSet name="shipping">
<TextInput name="street" :style="{ height: 24 }" />
<TextInput name="city" :style="{ height: 24 }" />
</FieldSet>
</Page>
</Document>
</template> The fields above export as billing.street, billing.city, shipping.street, and shipping.city.
<TextInput> and <Checkbox> are namespaced by their <FieldSet>. <Select> and <List> always register at the top level, so give them globally unique names. <FieldSet> also cannot be nested inside another <FieldSet> — use one level of grouping per field. Value Formatting
<TextInput> accepts a format object that tells the reader how to display and validate what is typed:
<template>
<Document>
<Page size="A4" :style="{ padding: 40 }">
<!-- Formatted as a date -->
<TextInput
name="dob"
:format="{ type: 'date', param: 'dd/mm/yyyy' }"
:style="{ height: 24 }"
/>
<!-- Formatted as currency -->
<TextInput
name="amount"
:format="{
type: 'number',
nDec: 2,
sepComma: true,
currency: '$',
currencyPrepend: true,
}"
:style="{ height: 24 }"
/>
</Page>
</Document>
</template>Fonts
A form needs at least one embedded font to draw field text. vue-pdf uses whatever font is active where the field is rendered, so the standard PDF fonts work out of the box. If you want your fields to match a custom typeface, register it and set fontFamily on an ancestor:
<script setup lang="ts">
import { Font } from '@vuepdf/renderer'
Font.register({
family: 'Open Sans',
src: 'https://fonts.gstatic.com/s/opensans/v17/mem8YaGs126MiZpBA-UFVZ0e.ttf',
})
</script>
<template>
<Document>
<Page size="A4" :style="{ padding: 40, fontFamily: 'Open Sans' }">
<TextInput name="fullName" :style="{ height: 24 }" />
</Page>
</Document>
</template>Next Steps
See the Interactive Form example for a complete, styled document, or jump into the per-component references linked at the top of this page.