惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

罗磊的独立博客
The GitHub Blog
The GitHub Blog
Hugging Face - Blog
Hugging Face - Blog
博客园 - 聂微东
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
IT之家
IT之家
小众软件
小众软件
博客园_首页
G
Google Developers Blog
Apple Machine Learning Research
Apple Machine Learning Research
MyScale Blog
MyScale Blog
Engineering at Meta
Engineering at Meta
Jina AI
Jina AI
酷 壳 – CoolShell
酷 壳 – CoolShell
人人都是产品经理
人人都是产品经理
B
Blog RSS Feed
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
D
Docker
B
Blog
雷峰网
雷峰网
WordPress大学
WordPress大学
Stack Overflow Blog
Stack Overflow Blog
宝玉的分享
宝玉的分享

Inside Nutrient

A guide to the invisible work behind documents Introducing Nutrient Documents for Salesforce: Native document generation and signing Document AI vs. traditional OCR: Choosing between OCR, AI, and hybrid pipelines PDF SDK compliance and security evaluation checklist for enterprise teams (2026) Invariant Corp replaces paper processes with Nutrient Workflow and scales without limits What is process mapping? A complete guide Nutrient vs. Conga Composer for Salesforce document generation (2026) Document routing: How to automate document distribution The CTO’s AI playbook: Why accountability architecture beats orchestration Compliance workflow automation: Why built-in compliance is table stakes Workflow diagrams: Examples, symbols, and how to build one that actually runs Digital forms: Replace paper forms with automated workflows Approval workflow software: How to automate approvals Why document-centric automation is different The CEO’s AI playbook: Why decision architecture beats model selection Nutrient SDK product updates for Q1 2026 PDF redaction verification: How to prove sensitive data is permanently removed What is a VPAT? The complete guide to accessibility conformance reports What is PDF/UA? The accessible PDF standard explained Salesforce eSignatures: Generate, sign, and track documents in one flow Online document viewer: Options, tradeoffs, and how to embed one Document viewer for web apps: React, Vue, Angular (2026) Best document viewers in 2026: A buyer’s guide How to edit a PDF in Python: Add text, images, and annotations Nutrient advances Workflow platform with agentic AI for enterprise-grade speed and consistency in document-heavy operations How to create a Salesforce quote template from opportunity data The business case for accessibility: Five ways it drives enterprise value Python PDF library comparison (2026): 7 libraries for developers Why your AI agent hallucinates PDF table data PDF.js limitations: When to upgrade to a commercial PDF SDK
How to make PDFs fillable
Hulya Masharipov · 2024-11-08 · via Inside Nutrient

Table of contents

    This tutorial compares different approaches to creating fillable PDFs — from open source libraries to enterprise solutions like Nutrient. It’ll cover the technical implementation and evaluate which tools deliver production-ready results quickly versus those requiring extensive custom development.

    How to make PDFs fillable

    Fillable PDFs reduce data entry errors, speed up document processing, and support remote collaboration. If you’re creating forms, contracts, or surveys, choosing the right PDF form builder saves significant development time.

    TL;DR

    This guide covers three approaches to creating fillable PDFs: PDF.js for viewing and filling existing forms, pdf-lib for programmatic PDF manipulation, and Nutrient Web SDK for complete form solutions with built-in UI, validation, and eSignatures.

    1. How to make PDFs fillable using open source libraries

    To make PDFs fillable, you need software capable of defining interactive fields within a document. Several open source libraries can handle this task, each with different capabilities and limitations.

    PDF.js

    PDF.js(opens in a new tab) is a popular library developed by Mozilla for rendering PDF documents in web browsers. It natively supports displaying and filling existing PDF forms. If your PDF already has form fields, PDF.js will render them and allow users to fill them out directly in the browser.

    However, PDF.js cannot programmatically create new form fields on a PDF that doesn’t already have them. While you could manually overlay HTML input fields on the canvas, this approach is complex and the data entered doesn’t actually become part of the PDF — it’s just floating on top of the rendered image.

    PDF.js works well for displaying and filling existing PDF forms. However, creating new form fields requires extensive custom JavaScript, CSS positioning, and event handling. The user experience with custom overlays often feels disconnected from the PDF itself.

    pdf-lib

    pdf-lib(opens in a new tab) is a versatile library for creating and modifying PDF documents, and it works seamlessly in both Node.js and browser environments.

    Step 1 — Installing pdf-lib

    If you’re using Node.js, install pdf-lib via npm:

    Step 2 — Listing available form fields

    First, find out what fields exist in your PDF. Save this file with an .mjs extension (e.g. listFields.mjs):

    import { PDFDocument } from "pdf-lib";

    import fs from "fs";

    async function listFields() {

    const existingPdfBytes = fs.readFileSync("sample_pdf.pdf");

    const pdfDoc = await PDFDocument.load(existingPdfBytes);

    const form = pdfDoc.getForm();

    const fields = form.getFields();

    console.log("Available form fields:");

    fields.forEach((field) => {

    const name = field.getName();

    const type = field.constructor.name;

    console.log(`- ${name} (${type})`);

    });

    }

    listFields();

    Run with: node listFields.mjs

    Step 3 — Filling form fields

    Once you know the field names, use the appropriate method based on the field type:

    import { PDFDocument } from "pdf-lib";

    import fs from "fs";

    async function fillPdf() {

    const existingPdfBytes = fs.readFileSync("sample_pdf.pdf");

    const pdfDoc = await PDFDocument.load(existingPdfBytes);

    const form = pdfDoc.getForm();

    // For `PDFTextField` — use the exact field name from Step 2.

    const textField = form.getTextField("TEXT_FIELD_NAME");

    textField.setText("Your value here");

    // For `PDFCheckBox`.

    const checkbox = form.getCheckBox("CHECKBOX_FIELD_NAME");

    checkbox.check(); // Or `checkbox.uncheck()`.

    // For `PDFRadioGroup`.

    const radioGroup = form.getRadioGroup("RADIO_FIELD_NAME");

    radioGroup.select("option1"); // Select one of the radio options.

    // For `PDFDropdown`.

    const dropdown = form.getDropdown("DROPDOWN_FIELD_NAME");

    dropdown.select("optionValue");

    const pdfBytes = await pdfDoc.save();

    fs.writeFileSync("filled.pdf", pdfBytes);

    }

    fillPdf();

    Run with: node fillPdf.mjs

    Use only the methods that match your PDF’s field types. PDFButton fields are typically for actions, not data entry.

    pdf-lib works for developers comfortable writing custom code, but you’ll need to build field focus management, error handling, validation, and data extraction yourself. There are no built-in user interface (UI) components.

    2. How to make PDFs fillable with advanced features using Nutrient

    Nutrient Web SDK provides a JavaScript library for generating, customizing, and managing PDF forms programmatically. This section covers how to create various form fields and customize them.

    Explore the Nutrient demo

    Prerequisites

    You need a valid Nutrient license with Form Creator support (version 2019.5 or newer) for creating form fields. For adding form fields using the UI, you’ll need version 2022.3 or later.

    Step 1 — Installation

    Choose either CDN or npm installation.

    Option A: CDN (quickest)

    <!DOCTYPE html>

    <html>

    <head>

    <title>Nutrient Web SDK</title>

    </head>

    <body>

    <script src="https://cdn.cloud.pspdfkit.com/pspdfkit-web@1.16.1/nutrient-viewer.js"></script>

    <div id="pdf-viewer" style="width: 100%; height: 100vh;"></div>

    <script src="index.js"></script>

    </body>

    </html>

    Option B: npm package

    npm install @nutrient-sdk/viewer

    Step 2 — Loading Nutrient

    Initialize Nutrient Web SDK and load a PDF:

    const container = document.getElementById("pdf-viewer");

    // For CDN installation.

    const { NutrientViewer } = window;

    // For npm installation, import at the top of your file:

    // import NutrientViewer from "@nutrient-sdk/viewer";

    if (container && NutrientViewer) {

    NutrientViewer.load({

    container,

    document: "document.pdf",

    })

    .then((instance) => {

    console.log("Nutrient loaded", instance);

    })

    .catch((error) => {

    console.error(error.message);

    });

    }

    Step 3 — Creating a text form field

    Use NutrientViewer.Annotations.WidgetAnnotation for the widget and NutrientViewer.FormFields.TextFormField for the form field:

    const widget = new NutrientViewer.Annotations.WidgetAnnotation({

    id: NutrientViewer.generateInstantId(),

    pageIndex: 0,

    formFieldName: "MyFormField",

    boundingBox: new NutrientViewer.Geometry.Rect({

    left: 100,

    top: 75,

    width: 200,

    height: 80,

    }),

    });

    const textFormField = new NutrientViewer.FormFields.TextFormField({

    name: "MyFormField",

    annotationIds: new NutrientViewer.Immutable.List([widget.id]),

    value: "Text shown in the form field",

    });

    instance.create([widget, textFormField]);

    Step 4 — Creating radio buttons

    Create multiple widgets with the same form field name:

    const radioWidget1 = new NutrientViewer.Annotations.WidgetAnnotation({

    id: NutrientViewer.generateInstantId(),

    pageIndex: 0,

    formFieldName: "MyRadioField",

    boundingBox: new NutrientViewer.Geometry.Rect({

    left: 100,

    top: 170,

    width: 20,

    height: 20,

    }),

    });

    const radioWidget2 = new NutrientViewer.Annotations.WidgetAnnotation({

    id: NutrientViewer.generateInstantId(),

    pageIndex: 0,

    formFieldName: "MyRadioField",

    boundingBox: new NutrientViewer.Geometry.Rect({

    left: 130,

    top: 170,

    width: 20,

    height: 20,

    }),

    });

    const radioFormField = new NutrientViewer.FormFields.RadioButtonFormField({

    name: "MyRadioField",

    annotationIds: new NutrientViewer.Immutable.List([

    radioWidget1.id,

    radioWidget2.id,

    ]),

    options: new NutrientViewer.Immutable.List([

    new NutrientViewer.FormOption({

    label: "Option 1",

    value: "1",

    }),

    new NutrientViewer.FormOption({

    label: "Option 2",

    value: "2",

    }),

    ]),

    defaultValue: "1",

    });

    instance.create([radioWidget1, radioWidget2, radioFormField]);

    Step 5 — Enable form design mode

    Allow users to adjust the placement of form elements:

    instance.setViewState((viewState) => viewState.set("formDesignMode", true));

    Here’s the full code combining all steps:

    const container = document.getElementById("pdf-viewer");

    86 collapsed lines

    const { NutrientViewer } = window; // For CDN installation

    if (container && NutrientViewer) {

    NutrientViewer.load({

    container,

    document: "document.pdf",

    })

    .then((instance) => {

    console.log("Nutrient loaded", instance);

    // Create a text form field.

    const widget = new NutrientViewer.Annotations.WidgetAnnotation({

    id: NutrientViewer.generateInstantId(),

    pageIndex: 0,

    formFieldName: "MyFormField",

    boundingBox: new NutrientViewer.Geometry.Rect({

    left: 100,

    top: 75,

    width: 200,

    height: 80,

    }),

    });

    const textFormField = new NutrientViewer.FormFields.TextFormField({

    name: "MyFormField",

    annotationIds: new NutrientViewer.Immutable.List([widget.id]),

    value: "Text shown in the form field",

    });

    instance.create([widget, textFormField]);

    // Create radio button form field with two options.

    const radioWidget1 = new NutrientViewer.Annotations.WidgetAnnotation({

    id: NutrientViewer.generateInstantId(),

    pageIndex: 0,

    formFieldName: "MyRadioField",

    boundingBox: new NutrientViewer.Geometry.Rect({

    left: 100,

    top: 170,

    width: 20,

    height: 20,

    }),

    });

    const radioWidget2 = new NutrientViewer.Annotations.WidgetAnnotation({

    id: NutrientViewer.generateInstantId(),

    pageIndex: 0,

    formFieldName: "MyRadioField",

    boundingBox: new NutrientViewer.Geometry.Rect({

    left: 130,

    top: 170,

    width: 20,

    height: 20,

    }),

    });

    const radioFormField = new NutrientViewer.FormFields.RadioButtonFormField(

    {

    name: "MyRadioField",

    annotationIds: new NutrientViewer.Immutable.List([

    radioWidget1.id,

    radioWidget2.id,

    ]),

    options: new NutrientViewer.Immutable.List([

    new NutrientViewer.FormOption({

    label: "Option 1",

    value: "1",

    }),

    new NutrientViewer.FormOption({

    label: "Option 2",

    value: "2",

    }),

    ]),

    defaultValue: "1",

    },

    );

    instance.create([radioWidget1, radioWidget2, radioFormField]);

    // Enable form design mode.

    instance.setViewState((viewState) =>

    viewState.set("formDesignMode", true),

    );

    })

    .catch((error) => {

    console.error("Error loading Nutrient:", error.message);

    });

    }

    FeaturePDF.jspdf-libNutrient Web SDK
    Fill existing forms
    Create new form fieldsManual overlay only
    Built-in UI
    Form validationCustomCustomBuilt-in
    eSignatures
    Data extractionCustomCustomBuilt-in
    Mobile supportBasicBasicOptimized
    Best forViewing existing formsProgrammatic manipulationProduction applications

    Best practices to make PDFs fillable

    1. Use a clear and consistent layout — Organized layouts make forms easy to read and navigate.
    2. Use clear and concise language — Form labels and instructions should be straightforward.
    3. Use headings and subheadings — Organize forms with headings for easy scanning.
    4. Use bullet points and numbered lists — These elements improve readability.
    5. Ensure form fields are large enough — Fields should be sized appropriately for comfortable data entry.
    6. Clearly label the submit button — Make submission buttons easy to find with clear labels.
    7. Test your form — Verify forms work correctly and users can complete and submit them.

    Conclusion

    Free libraries like PDF.js and pdf-lib work well for simple use cases and prototypes. For production applications requiring form creation, validation, eSignatures, and a complete user interface, Nutrient Web SDK provides these capabilities out of the box.

    Try Nutrient’s demo to explore the available features, or contact our Sales team to discuss your specific requirements.

    FAQ

    Fillable PDFs are documents that allow users to enter information directly into designated fields, making data entry easier and more efficient.

    Fillable PDFs enable easy data capture and sharing, reducing the challenges of paper-based forms.

    You can create fillable PDFs using various tools, including open source libraries like pdf-lib, or commercial solutions like Nutrient (formerly PSPDFKit).

    Yes. You can create fillable PDFs using JavaScript libraries such as pdf-lib and Nutrient, which provide functionality for adding interactive form fields.

    Some libraries may have limitations in terms of features or ease of use, requiring custom coding for field management or a deeper understanding of PDF structures for advanced functionalities.

    Explore related topics

    Try for free Ready to get started?

    Related SDK articles

    Explore more