Core Concepts

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:

vue
<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>
Form fields have no intrinsic size. A field without a 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:

PropTypeDefaultDescription
namestring''Field identifier. This is the key the value is exported under.
valuestring | numberThe field's current value.
defaultValuestring | numberThe value the field reverts to when the form is reset.
requiredbooleanfalseMarks the field as mandatory before the form can be submitted.
readOnlybooleanfalseDisplays the value but prevents the reader from editing it.
noExportbooleanfalseExcludes the field from the exported form data.
Boolean props work as bare attributes, the way they do on regular HTML elements — <Checkbox checked /> is the same as <Checkbox :checked="true" />. Multi-word props accept either casing: read-only and readOnly are equivalent.

Text Fields

vue
<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

vue
<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.

vue
<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:

vue
<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.

Only <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:

vue
<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>
Formatting is implemented as embedded JavaScript in the PDF. Acrobat runs it; many other readers — including most browser viewers — do not. Treat formatting as a progressive enhancement and never rely on it to validate data you actually care about.

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:

vue
<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.