Skip to content
ABCrimson edited this page Mar 1, 2026 · 5 revisions

Forms

modern-pdf-lib v0.15.1

modern-pdf-lib provides a complete AcroForm API. You can read, fill, create, and flatten PDF interactive forms. All seven AcroForm field types from the PDF specification are supported.


Field types

Type Class PDF /FT /Ff flags
Text field PdfTextField Tx
Checkbox PdfCheckboxField Btn Pushbutton=0, Radio=0
Radio group PdfRadioGroup Btn Radio=1
Dropdown (combobox) PdfDropdownField Ch Combo=1
Listbox PdfListboxField Ch Combo=0
Button (push-button) PdfButtonField Btn Pushbutton=1
Signature field PdfSignatureField Sig

Getting the form

import { loadPdf } from 'modern-pdf-lib';

const doc  = await loadPdf(pdfBytes);
const form = doc.getForm();

getForm() returns a PdfForm instance. If the document has no /AcroForm entry an empty form is returned (all create* methods still work).


Reading field values

// Typed accessors — throw if the field is missing or the wrong type
const name    = form.getTextField('customer.name').getText();
const agreed  = form.getCheckbox('terms.agree').isChecked();
const country = form.getDropdown('address.country').getSelected();
const sizes   = form.getListbox('sizes').getSelected();    // string[]
const group   = form.getRadioGroup('payment').getSelected(); // option value

// Generic accessor — returns PdfField | undefined
const field = form.getField('myField');
if (field !== undefined) {
  console.log(field.fieldType, field.getFullName());
}

// All fields
const allFields = form.getFields();

Setting field values

// Text
form.getTextField('name').setText('Jane Doe');

// Checkbox
form.getCheckbox('subscribe').check();
form.getCheckbox('subscribe').uncheck();

// Radio group — pass the export value of the option to select
form.getRadioGroup('payment').select('credit-card');

// Dropdown — pass the display text of the option
form.getDropdown('country').select('Canada');

// Listbox — single selection (or multiple if multiselect is enabled)
form.getListbox('interests').select(['music', 'travel']);

// Button — set an image (for image buttons)
const image = await doc.embedPng(pngBytes);
form.getButton('logo').setImage(image);

Bulk fill

Fill multiple fields in one call using a plain object:

form.fill({
  'customer.name':     'Jane Doe',
  'customer.email':    'jane@example.com',
  'address.country':   'Canada',
  'terms.agree':       true,
});

Values are coerced by field type: string values are applied to text, dropdown, listbox, and radio fields; boolean values are applied to checkboxes.


Flattening

Flattening converts an interactive form to static page content. After flattening, field widgets are no longer interactive — their appearance streams become ordinary page content.

// Fill then flatten
form.getTextField('name').setText('Jane Doe');
form.getCheckbox('agree').check();
form.flatten();

const flatBytes = await doc.save();

flatten() internally:

  1. Calls generateAppearance() on every field to ensure all fields have up-to-date appearance streams.
  2. Clears the /Fields array in the /AcroForm dictionary so the document is no longer interactive.
  3. Removes the /NeedAppearances flag.

Creating fields programmatically

Use the createTextField(), createCheckbox(), and similar methods on PdfForm to add new fields to an existing document:

const page0 = 0; // zero-based page index

// Text field at [x1, y1, x2, y2] in page points
const nameField = form.createTextField(
  'recipient.name',  // field name
  page0,
  [50, 700, 250, 720],
);
nameField.setText('Default value');
nameField.setFontSize(12);

// Checkbox
const agreeBox = form.createCheckbox('terms.agree', page0, [50, 680, 65, 695]);
agreeBox.check();

// Dropdown with option list
const dropdown = form.createDropdown(
  'country',
  page0,
  [50, 650, 200, 668],
  ['Canada', 'France', 'Germany', 'Japan', 'United States'],
);
dropdown.select('Canada');

addToPage()

Individual field instances can also be placed on a page after creation using addToPage():

const field = form.createTextField('notes', 0, [50, 500, 400, 560]);
field.setMultiline(true);
field.addToPage(doc.getPage(1), { x: 50, y: 300, width: 400, height: 60 });

This places an additional widget annotation for the same logical field on page 1 (one field, multiple widgets — valid per the PDF specification).


Custom appearance providers

By default, modern-pdf-lib generates standard appearance streams for all field types using the font and size from the field's /DA (default appearance) string. You can override this with a custom appearance provider.

import type { AppearanceProviderFor } from 'modern-pdf-lib';
import type { PdfTextField } from 'modern-pdf-lib';

const myProvider: AppearanceProviderFor<PdfTextField> = (field, widget, doc) => {
  // widget is the PdfDict for the widget annotation
  // Return an array of PDF content stream operators as strings, or
  // return undefined to fall back to the default appearance.
  const text = field.getText() ?? '';
  return [
    'q',
    '0.9 0.9 0.9 rg',          // light grey background
    `0 0 ${widget.width} ${widget.height} re f`,
    'Q',
    `BT /Helv 11 Tf 0 g 4 4 Td (${text}) Tj ET`,
  ];
};

// Apply the provider
form.getTextField('notes').setAppearanceProvider(myProvider);

AppearanceProviderFor<T> is a generic type alias:

type AppearanceProviderFor<T extends PdfField> =
  (field: T, widget: PdfDict, doc: PdfDocument) => string[] | undefined;

Exporting field values

Individual fields can be marked as exported or non-exported. Non-exported fields are excluded when the browser or PDF viewer submits the form:

const field = form.getTextField('internal-notes');
field.isExported();           // boolean
field.disableExporting();     // sets the NoExport flag
field.enableExporting();      // clears the NoExport flag

XFA handling

Some older PDF forms use XFA (XML Forms Architecture) alongside or instead of AcroForm. XFA causes PDF viewers to use an entirely different rendering engine that ignores the AcroForm fields.

// Check for XFA
if (form.hasXFA()) {
  console.warn('This form uses XFA — deleting to force AcroForm rendering.');
  form.deleteXFA();
  // deleteXFA() also sets /NeedAppearances to true so the viewer
  // knows it must regenerate appearances for the AcroForm fields.
}

After deleteXFA(), the document behaves as a standard AcroForm document.


Field properties reference

PdfTextField

Method Description
getText() Get the current text value
setText(v) Set the text value
setMaxLength(n) Set /MaxLen
setMultiline(b) Enable/disable multiline (Multiline flag)
setPassword(b) Enable/disable password masking (Password flag)
setFileSelect(b) Enable/disable file-select mode
setFontSize(n) Set the font size in the /DA string

PdfCheckboxField

Method Description
isChecked() Returns true if the checkbox is checked
check() Set value to the on-state (/Yes or export value)
uncheck() Set value to /Off

PdfRadioGroup

Method Description
getSelected() Export value of the selected option
select(value) Select the option with this export value
getOptions() All export values in the group

PdfDropdownField / PdfListboxField

Method Description
getSelected() Currently selected option display text
select(text) Select by display text
getOptions() All { display, value } option pairs
setOptions(opts) Replace the option list

PdfButtonField

Method Description
setImage(image) Embed an image as the button face
click() Simulate a click (triggers JavaScript if present)

Common (PdfField base class)

Method Description
getName() Partial field name (/T)
getFullName() Dot-separated fully qualified name
fieldType Discriminant: 'text' | 'checkbox' | 'radio' | …
isReadOnly() ReadOnly flag
isRequired() Required flag
isExported() Not set: exported; NoExport set: not exported
enableExporting() Clear NoExport flag
disableExporting() Set NoExport flag
generateAppearance() Force appearance stream regeneration
addToPage(page, rect) Add a widget annotation to a page
setValue(v) Generic setter (string or boolean)

Clone this wiki locally