> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/web/get-started/samples/showcase-demo-pdf-forms.md).

# PDF Forms Showcase Demo Code Sample

Use a JSON file with field values to programmatically populate and extract data from editable PDF forms.

{% hint style="info" %}
**Requirements**

*These packages are required to use these features in production. Trial keys have unlimited access to all features*

<a href="/web/get-started/readme.md" class="button primary">Web SDK</a><a href="https://showcase.apryse.com/pdf-forms" class="button primary">Live demo</a>
{% endhint %}

Use a JSON file with field values to programmatically populate and extract data from editable PDF forms.

This demo allows you to:

* Upload your own PDF form
* Use a sample JSON to input data into the sample PDF form
* Add annotations and customize contents
* Edit the layout

### **Implementation steps**

To add PDF Form capability with WebViewer:

Step 1: [Get started with WebViewer](/web/get-started/readme.md) in your preferred web stack Step 2: Add the ES6 JavaScript sample code provided in this guide

Once you generate your license key, it will automatically be included in your sample code below.

{% @apryse-license-key/apryse-license-key platform="WEB\_VIEWER" variant="compact" %}

<pre class="language-js" data-line-numbers><code class="lang-js">// ES6 Compliant Syntax
// Copilot name: GitHub Copilot, version: 1.0.0, model: GPT-4, version: 2024-06, date: 2025-09-18
// File: pdf-form/index.js

import WebViewer from '@pdftron/webviewer';

const licenseKey = '<code class="expression">visitor.claims.wvKey || "YOUR_WEBVIEWER_LICENSE_KEY"</code>';

const initialDoc = 'https://apryse.s3.us-west-1.amazonaws.com/public/files/samples/form-1040.pdf';

// Sample JSON data to fill the form-1040.pdf
const sampleJSONData = {
  "topmostSubform[0].Page1[0].f1_04[0]": "Jennifer F.",
  "topmostSubform[0].Page1[0].f1_05[0]": "Ng",
  "topmostSubform[0].Page1[0].f1_06[0]": "0100101",
  "topmostSubform[0].Page1[0].f1_07[0]": "Dana",
  "topmostSubform[0].Page1[0].f1_08[0]": "York",
  "topmostSubform[0].Page1[0].SpouseSSN[0]": "1111111",
  "topmostSubform[0].Page1[0].SpouseSSN[0].f1_09[0]": "1111111",
  "topmostSubform[0].Page1[0].Address[0]": "hi",
  "topmostSubform[0].Page1[0].Address[0].f1-10[0]": "123 Broadway Avenue",
  "topmostSubform[0].Page1[0].Address[0].f1-11[0]": "24",
  "topmostSubform[0].Page1[0].Address[0].f1-12[0]": "Los Angeles, California, 90210",
  "topmostSubform[0].Page1[0].Address[0].f1_13[0]": "Canada",
  "topmostSubform[0].Page1[0].Address[0].f1_14[0]": "British Columbia",
  "topmostSubform[0].Page1[0].Address[0].f1_15[0]": "V6T1PX",
  "topmostSubform[0].Page1[0].c1_01[0]": "1",
  "topmostSubform[0].Page1[0].c1_02[0]": "Off",
  "topmostSubform[0].Page1[0].Lines1-3[0]": "2",
  "topmostSubform[0].Page1[0].Lines1-3[0].c1_03[0]": "Off",
  "topmostSubform[0].Page1[0].Lines1-3[0].c1_03[1]": "Off",
  "topmostSubform[0].Page1[0].Lines1-3[0].c1_03[2]": "Off",
  "topmostSubform[0].Page1[0].Lines1-3[0].f1-16[0]": "Off"
};

// Temporary JSON data to hold the live form data
let tempJSONData = {};

// The list of registered panels in the main webviewer
let viewerPanels = null;

// The tab panel, representing the webviewer left panel
const tabPanel = {
  handle: null,
  dataElement: 'tabPanel'
};

// The custom form sub-panel to be registered
const formPanel = {
  handle: null,
  dataElement: 'formPanel',
  render: null,
};

WebViewer(
  {
    path: '/lib',
    fullAPI: true, // This is required to download the pdf with flattening contents.
    initialDoc: initialDoc,
    enableFilePicker: true, // Enable file picker to open files. In WebViewer -> menu icon -> Open File
    enableMeasurement: true,
    licenseKey: licenseKey, // Replace with your license key
  },
  document.getElementById('viewer')
).then((instance) => {
  const { documentViewer, annotationManager } = instance.Core;

  // Customize the main webviewer left panel after the load completion.
  // The left panel will display the template sub-panel with fields.
  documentViewer.addEventListener('documentLoaded', () => {
    customizeUI(instance);

    // Reset the temporary JSON data when a new document is loaded
    tempJSONData = {};
  });

  // Capture form field changes and update the temporary JSON data
  annotationManager.addEventListener('fieldChanged', (field, value) => {
    if (tempJSONData[field.name])
      tempJSONData[field.name] = value;
    else
      tempJSONData = { ...tempJSONData, [field.name]: value };

    // Update the scroll box text content with the latest JSON data
    const scrollBoxText = formPanel.render.querySelector("#scrollBox").querySelector("pre");
    scrollBoxText.textContent = JSON.stringify(tempJSONData, null, 2);

    // Enable the "Open in Dialog" button
    enableButton(formPanel.render.querySelector("#jsonDataDialogButton"), true);
  });

  console.log('✅ WebViewer loaded successfully.');
}).catch((error) => {
  console.error('❌ Failed to initialize WebViewer:', error);
});

// Customize the main webviewer left panel after the load completion
const customizeUI = (instance) => {
  const { UI } = instance;

  // Close the tab panel (if it's open) for refreshment.
  UI.closeElements([tabPanel.dataElement]);

  // Get the list of registered panels in the main webviewer
  viewerPanels = UI.getPanels();

  // Find the Tab Panel to modify. The template sub-panel will be added to this Tab panel.
  tabPanel.handle = viewerPanels.find((panel) => panel.dataElement === tabPanel.dataElement);

  // Register the custom form sub-panel
  RegisterFormPanel(instance);

  // Add the new custom form sub-panel to list of sub-panels under the Tab Panel
  formPanel.handle = { render: formPanel.dataElement };
  tabPanel.handle.panelsList = [formPanel.handle, ...tabPanel.handle.panelsList];

  UI.openElements([tabPanel.dataElement]);
  UI.setPanelWidth(tabPanel.dataElement, 400);
};

// Register the custom form sub-panel
const RegisterFormPanel = (instance) => {
  formPanel.render = createFormPanelElements(instance);
  instance.UI.addPanel({
    dataElement: formPanel.dataElement,
    location: 'left',
    icon: '&#x3C;svg width="18px" height="18px" viewBox="0 0 24 24" id="圖層_1" data-name="圖層 1" xmlns="http://www.w3.org/2000/svg">&#x3C;defs>&#x3C;style>.cls-1{fill:#080808;}&#x3C;/style>&#x3C;/defs>&#x3C;title>template&#x3C;/title>&#x3C;path class="cls-1" d="M21,.5H3a2,2,0,0,0-2,2V22a2,2,0,0,0,2,2H21a2,2,0,0,0,2-2V2.5A2,2,0,0,0,21,.5Zm0,2v2H3v-2ZM3,22V6.5H21V22Z"/>&#x3C;path class="cls-1" d="M12.5,4H20a.5.5,0,0,0,0-1H12.5a.5.5,0,0,0,0,1Z"/>&#x3C;path class="cls-1" d="M4.5,4a.43.43,0,0,0,.19,0,.35.35,0,0,0,.16-.11A.47.47,0,0,0,5,3.5a.43.43,0,0,0,0-.19.36.36,0,0,0-.11-.16.5.5,0,0,0-.7,0A.35.35,0,0,0,4,3.31.43.43,0,0,0,4,3.5a.51.51,0,0,0,.5.5Z"/>&#x3C;path class="cls-1" d="M5.65,3.85A.36.36,0,0,0,5.81,4,.44.44,0,0,0,6,4a.47.47,0,0,0,.35-.15.36.36,0,0,0,.11-.16.6.6,0,0,0,0-.19.51.51,0,0,0-.15-.35A.49.49,0,0,0,5.81,3a.36.36,0,0,0-.16.11.47.47,0,0,0-.15.35.4.4,0,0,0,0,.19A.35.35,0,0,0,5.65,3.85Z"/>&#x3C;path class="cls-1" d="M8,8H4.5a1,1,0,0,0,0,2H8A1,1,0,0,0,8,8Z"/>&#x3C;path class="cls-1" d="M8,11.67H4.5a1,1,0,0,0,0,2H8a1,1,0,0,0,0-2Z"/>&#x3C;path class="cls-1" d="M8,15.33H4.5a1,1,0,0,0,0,2H8a1,1,0,0,0,0-2Z"/>&#x3C;path class="cls-1" d="M8,19H4.5a1,1,0,0,0,0,2H8a1,1,0,0,0,0-2Z"/>&#x3C;path class="cls-1" d="M14,8H10.5a1,1,0,0,0,0,2H14a1,1,0,0,0,0-2Z"/>&#x3C;path class="cls-1" d="M14,11.67H10.5a1,1,0,0,0,0,2H14a1,1,0,0,0,0-2Z"/>&#x3C;path class="cls-1" d="M14,15.33H10.5a1,1,0,0,0,0,2H14a1,1,0,0,0,0-2Z"/>&#x3C;path class="cls-1" d="M14,19H10.5a1,1,0,0,0,0,2H14a1,1,0,0,0,0-2Z"/>&#x3C;path class="cls-1" d="M19.5,8h-3a1,1,0,0,0,0,2h3a1,1,0,0,0,0-2Z"/>&#x3C;path class="cls-1" d="M19.5,11.67h-3a1,1,0,0,0,0,2h3a1,1,0,0,0,0-2Z"/>&#x3C;path class="cls-1" d="M19.5,15.33h-3a1,1,0,0,0,0,2h3a1,1,0,0,0,0-2Z"/>&#x3C;path class="cls-1" d="M19.5,19h-3a1,1,0,0,0,0,2h3a1,1,0,0,0,0-2Z"/>&#x3C;/svg>',
    title: 'Form',
    render: () => formPanel.render,
  });
};

// Create the form panel elements.
// The elements will be created dynamically
// when the form panel is registered after
// loading the document.
const createFormPanelElements = (instance) => {
  let panelDiv = document.createElement('div');
  panelDiv.id = 'form';
  let paragraph = document.createTextNode('A demo of the PDF form capabilities in WebViewer, a JavaScript-based PDF SDK for web apps. Programmatically fill and extract data from forms with JavaScript.');
  panelDiv.appendChild(paragraph);

  let dividerDiv = document.createElement('div');
  dividerDiv.style.borderTop = '1px solid #ccc';
  dividerDiv.style.margin = '10px 0';
  panelDiv.appendChild(dividerDiv);

  // Format and insert JSON data
  const scrollBox = document.createElement("div");
  scrollBox.id = 'scrollBox';
  scrollBox.style.width = "350px";
  scrollBox.style.height = "350px";
  scrollBox.style.border = "2px solid #444";
  scrollBox.style.overflow = "scroll"; // Enables both vertical and horizontal scroll
  scrollBox.style.whiteSpace = "nowrap"; // Prevents wrapping for horizontal scroll
  scrollBox.style.padding = "10px";
  scrollBox.style.fontFamily = "monospace";
  scrollBox.style.backgroundColor = "black";
  scrollBox.style.color = "white";

  const jsonContent = document.createElement("pre");
  jsonContent.textContent = 'Change a form field to see live data!';
  scrollBox.appendChild(jsonContent);
  panelDiv.appendChild(scrollBox);

  // Open JSON data dialog button
  let jsonDataDialogButton = document.createElement('button');
  jsonDataDialogButton.textContent = 'Open in Dialog';
  jsonDataDialogButton.id = 'jsonDataDialogButton';
  jsonDataDialogButton.onclick = () => openJsonDataDialog(JSON.stringify(tempJSONData, null, 2));
  panelDiv.appendChild(jsonDataDialogButton);
  panelDiv.appendChild(document.createElement('p'));
  enableButton(jsonDataDialogButton, false);

  // If the loaded document is form-1040.pdf, show the Sample JSON Data button
  const doc = instance.Core.documentViewer.getDocument();
  if (doc &#x26;&#x26; doc.filename === 'form-1040.pdf') {
    panelDiv.appendChild(dividerDiv.cloneNode());
    paragraph = document.createTextNode('Alternatively, fill your form with pre-existing JSON data.');
    panelDiv.appendChild(paragraph);

    // Sample JSON Data button
    let sampleJSONDataButton = document.createElement('button');
    sampleJSONDataButton.textContent = 'Sample JSON Data';
    sampleJSONDataButton.style.backgroundColor = 'blue';
    sampleJSONDataButton.style.color = 'white';
    sampleJSONDataButton.onclick = () => fillJsonDataDialog(instance);
    panelDiv.appendChild(sampleJSONDataButton);
  }

  // Downloading with flattening button
  let downloadingFlatteningButton = document.createElement('button');
  downloadingFlatteningButton.textContent = 'Downloading with flattening';
  downloadingFlatteningButton.style.backgroundColor = 'blue';
  downloadingFlatteningButton.style.color = 'white';
  downloadingFlatteningButton.onclick = () => instance.UI.downloadPdf({
    flags: instance.Core.SaveOptions.LINEARIZED,
    includeAnnotations: true,
    flatten: true
  });

  // Download only button
  let downloadButton = document.createElement('button');
  downloadButton.textContent = 'Download only';
  downloadButton.style.backgroundColor = 'blue';
  downloadButton.style.color = 'white';
  downloadButton.onclick = () => instance.UI.downloadPdf({
    flags: instance.Core.SaveOptions.LINEARIZED,
    includeAnnotations: true
  });

  panelDiv.appendChild(dividerDiv.cloneNode());
  paragraph = document.createElement('p');

  // Create a text node for the first part of the text
  const textBeforeLink = document.createTextNode('Optionally apply ');

  // Create an anchor (link) element
  const link = document.createElement('a');
  link.href = 'https://docs.apryse.com/web/guides/annotation/flatten-annotations';
  link.textContent = 'flattening';
  link.target = '_blank'; // Opens the link in a new tab

  // Create a text node for the text after the link
  const textAfterLink = document.createTextNode(' to permanently add field data.');

  // Append the text and link to the paragraph
  paragraph.appendChild(textBeforeLink);
  paragraph.appendChild(link);
  paragraph.appendChild(textAfterLink);
  panelDiv.appendChild(paragraph);

  panelDiv.appendChild(document.createElement('p'));
  panelDiv.appendChild(downloadingFlatteningButton);
  panelDiv.appendChild(document.createElement('p'));
  panelDiv.appendChild(downloadButton);

  return panelDiv;
};

// Enable or disable a button based on the state
const enableButton = (button, state) => {
  button.disabled = !state;
  button.style.backgroundColor = (state) ? 'blue' : 'gray';
  button.style.color = (state) ? 'white' : 'darkgray';
};

// Open JSON data in a dialog box with zoom in/out and close buttons
const openJsonDataDialog = (jsonText) => {
  let fontSize = 14;

  // Create overlay
  const overlay = document.createElement("div");
  overlay.className = "modal-overlay";
  overlay.onclick = (e) => {
    if (e.target === overlay) {
      document.body.removeChild(overlay);
    }
  };

  // Modal box
  const modal = document.createElement("div");
  modal.className = "modal-box";

  // Controls
  const controls = document.createElement("div");
  controls.className = "modal-controls";

  const title = document.createElement("span");
  title.textContent = "Form Data";
  title.style.fontWeight = "bold";
  title.style.marginRight = "auto";
  controls.appendChild(title);

  const zoomInBtn = document.createElement("button");
  zoomInBtn.textContent = "+";
  zoomInBtn.onclick = () => {
    fontSize += 2;
    content.style.fontSize = fontSize + "px";
  };

  const zoomOutBtn = document.createElement("button");
  zoomOutBtn.textContent = "-";
  zoomOutBtn.onclick = () => {
    fontSize = Math.max(10, fontSize - 2);
    content.style.fontSize = fontSize + "px";
  };

  const closeBtn = document.createElement("button");
  closeBtn.textContent = "Close";
  closeBtn.className = "modal-close";
  closeBtn.onclick = () => {
    document.body.removeChild(overlay);
  };

  controls.appendChild(zoomInBtn);
  controls.appendChild(zoomOutBtn);
  controls.appendChild(closeBtn);

  // Content
  const content = document.createElement("pre");
  content.className = "modal-content";
  content.style.fontSize = fontSize + "px";
  content.innerHTML = jsonText;

  modal.appendChild(controls);
  modal.appendChild(content);
  overlay.appendChild(modal);
  document.body.appendChild(overlay);
}

// Fill the form with sample JSON data in a dialog box with Fill and Close buttons
const fillJsonDataDialog = (instance) => {

  // Create overlay
  const overlay = document.createElement("div");
  overlay.className = "modal-overlay";
  overlay.onclick = (e) => {
    if (e.target === overlay) {
      document.body.removeChild(overlay);
    }
  };

  // Modal box
  const modal = document.createElement("div");
  modal.className = "modal-box";

  // Controls
  const controls = document.createElement("div");
  controls.className = "modal-controls";

  const title = document.createElement("span");
  title.textContent = "Sample JSON Data";
  title.style.fontWeight = "bold";
  title.style.marginRight = "auto";
  controls.appendChild(title);

  const fillBtn = document.createElement("button");
  fillBtn.textContent = "Fill";
  fillBtn.style.marginLeft = "475px";
  fillBtn.onclick = () => {
    const fieldManager = instance.Core.annotationManager.getFieldManager();
    Object.keys(sampleJSONData).forEach((key) => {
      const field = fieldManager.getField(key);
      field &#x26;&#x26; field.setValue(sampleJSONData[key]);
    });
    document.body.removeChild(overlay);
  };

  const closeBtn = document.createElement("button");
  closeBtn.textContent = "Close";
  closeBtn.className = "modal-close";
  closeBtn.onclick = () => {
    document.body.removeChild(overlay);
  };

  controls.appendChild(fillBtn);
  controls.appendChild(closeBtn);

  // Content
  const content = document.createElement("pre");
  content.className = "modal-content";
  content.style.fontSize = "14px";
  content.innerHTML = JSON.stringify(sampleJSONData, null, 2);

  modal.appendChild(controls);
  modal.appendChild(content);
  overlay.appendChild(modal);
  document.body.appendChild(overlay);
}
</code></pre>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/web/get-started/samples/showcase-demo-pdf-forms.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
