Edit PDF form fields using JavaScript

You can update PDF form fields in two ways:

  • Programmatically — Using the instance.update() method to modify form fields and their associated widget annotations. Useful when you need to automate form field updates, apply bulk changes, enforce business rules, or integrate with your application logic. This approach is ideal for workflows that require consistent, repeatable modifications across multiple documents or form fields.
  • Manually — Using the built-in user interface in the viewer to edit form fields. Useful when end users need to directly modify form fields through the viewer interface. This approach provides an intuitive visual experience for one-off edits or interactive form filling.

Understanding PDF form fields and widget annotations

PDF forms consist of two related components:

  • Form field object — Stores data and logic, such as readOnly and required.
  • Widget annotation — Displays the form field visually on the page and controls appearance, such as backgroundColor or fontColor.

In the PDF specification, widget annotations render form fields visually. A form field can link to one or more widget annotations, depending on how many times the field appears in the document. Understanding this relationship is important when you customize the appearance or behavior of form fields.

Programmatically editing form fields

The following example retrieves and updates a form field, along with its associated widget annotation:

const formFields = await instance.getFormFields();
const formField = formFields.find(
(formField) => formField.name === "my form field"
);
if (!formField) {
console.warn('Form field "my form field" not found');
return;
}
const annotations = await instance.getAnnotations(0);
const widget = annotations.find(
(annotation) => annotation.formFieldName === "my form field"
);
if (!widget) {
console.warn('Widget for "my form field" not found on page zero');
return;
}
await instance.update([
formField.set("required", true),
widget.set("opacity", 0.5)
]);

The example above demonstrates the following steps:

  1. Retrieve all form fields in the document.
  2. Search for the form field by name and validate it exists.
  3. Retrieve all annotations from page zero.
  4. Search for the widget annotation linked to the form field and validate it exists.
  5. Update the form field and widget annotation in a single operation.

Batch updating multiple form fields

When you need to update multiple form fields at once, batch your updates for better performance:

// Retrieve all form fields.
const formFields = await instance.getFormFields();
// Prepare batch updates.
const updates = [];
formFields.forEach((formField) => {
// Set specific fields to read-only.
if (formField.name === "customerName" || formField.name === "invoiceNumber") {
updates.push(formField.set("readOnly", true));
}
// Mark other fields as required.
if (formField.name === "email" || formField.name === "phone") {
updates.push(formField.set("required", true));
}
});
// Apply all updates in a single operation.
await instance.update(updates);

Handling multiple widgets per form field

A form field can have multiple widget annotations across different pages. When updating visual properties, make sure you update all associated widgets:

const formFields = await instance.getFormFields();
const formField = formFields.find((ff) => ff.name === "signature");
if (!formField) {
console.warn('Form field "signature" not found');
return;
}
// Collect all widgets for this form field across all pages.
const widgetUpdates = [];
for (let pageIndex = 0; pageIndex < instance.totalPageCount; pageIndex++) {
const annotations = await instance.getAnnotations(pageIndex);
// Find widgets that belong to this form field.
const widgets = annotations.filter(
(ann) =>
ann instanceof NutrientViewer.Annotations.WidgetAnnotation &&
formField.annotationIds.includes(ann.id)
);
// Update styling for each widget.
widgets.forEach((widget) => {
widgetUpdates.push(
widget
.set("backgroundColor", new NutrientViewer.Color({ r: 255, g: 255, b: 200 }))
.set("borderColor", new NutrientViewer.Color({ r: 0, g: 0, b: 0 }))
.set("borderWidth", 2)
);
});
}
// Apply all widget updates.
if (widgetUpdates.length > 0) {
await instance.update(widgetUpdates);
console.log(`Updated ${widgetUpdates.length} widget(s)`);
} else {
console.warn('No widgets found for form field "signature"');
}

When working with documents that have many pages, consider caching annotations to improve performance. Refer to the iterate over form fields knowledge base article for an optimized caching pattern that loads all page annotations in parallel.

Key properties by component type

The form field and widget annotation objects expose different sets of properties. When editing form fields, it’s important to know which properties belong to which component.

Form field properties

  • required — Marks the field as mandatory.
  • readOnly — Prevents users from editing the field.
  • noExport — Excludes field data from export operations.

For a complete list of form field types and their properties, refer to the form fields API documentation.

Widget annotation properties

  • Visual properties — backgroundColor, borderColor, fontColor
  • Layout properties — borderWidth, fontSize, opacity
  • Alignment — horizontalAlign, verticalAlign

For a complete list of widget annotation properties, refer to the widget annotation API documentation.