COMING SOON: Spring 2026 Release Arrives April 15th
Isaac Maw
Technical Content Creator
Published March 25, 2026
Updated March 26, 2026
5 min
Isaac Maw
Technical Content Creator

Summary: Understand how to implement PDF Editor functionality in your Node.js application with this blog, including sample code and documentation for Apryse WebViewer, our JavaScript document SDK that supports Node.js and other frameworks.
When you need to add PDF editing capability to your Node.js application, WebViewer offers the functionality you need to read, write, modify and edit PDF documents. In this article, we’ll take a look at how to get set up with the WebViewer SDK to add PDF editing in your Node.js application.

Node.js is a free, open-source JavaScript runtime environment that allows developers to execute JavaScript code outside a browser.
JavaScript is a core language for frontend web development, allowing developers to build dynamic and interactive elements in web applications. With Node.js, developers can also use JavaScript in the backend, allowing them to use one language across the entire stack, simplifying knowledge and team requirements, enabling reuse of certain code and logic, and shared libraries.
Node is fast, scalable and free, so it’s a popular choice for a variety of applications.
Try out WebViewer PDF Text Editing in the Apryse Showcase
Certain document processing functionality works best on the client-side, such as PDF viewing, while more complex functionality, such as programmatically editing a large number of PDF documents, runs best on the backend.
With WebViewer SDK and Node.js, you can utilize WebViewer functionality in your application, whether you prefer to execute the code in the browser with WebViewer with a framework such as React or Vite, or on the server using Node.js.
To get started with WebViewer for Node.js, you will need to:
You can watch this tutorial video to learn how to initialize and setup WebViewer for Node.js, then read on below for more details and code samples:
The following is Apryse sample code for using Apryse SDK to programmatically edit an existing PDF document's page display list and the graphics state attributes on existing elements.
In particular, this sample strips all images from the page and changes the text color to blue. You can also build a GUI with interactive PDF editor widgets. Some of Apryse SDK's other functions for programmatically editing PDFs include the Cos/SDF low-level API, page manipulation, and more. Visit the links to learn more about our Server SDK and PDF Editing & Manipulation Library.
Now that you’re set up with WebViewer and PDF Editing, you can change PDF document content without converting to other file types, enabling faster, more accurate document workflows without users leaving your application.
Check out our documentation to find details and walkthroughs on WebViewer features for:
If you’re ready to get started with WebViewer in production or have any questions about your trial, please contact sales.
Q: What is the best way to edit PDFs using Node.js?
A: Apryse provides a fully featured API that lets you programmatically modify text, images, and structure while preserving formatting. With support for Node.js, WebViewer SDK ensures high‑fidelity, secure document processing suitable for production workflows.
Q: Can I edit PDFs in the browser without converting them to another format?
A: Yes, Apryse WebViewer enables true in‑PDF editing directly in the browser (or on the backend using Node.js), eliminating conversions and maintaining exact layout integrity for a seamless WYSIWYG experience. This gives users intuitive, real‑time editing with reflowing text, custom styling, and robust annotation support.
Q: What’s the difference between client‑side and server‑side PDF editing?
A: Client‑side editing in WebViewer offers instant, interactive modifications inside the user’s browser, while server‑side editing with Apryse’s backend tools supports scalable, automated updates across large document sets. Both approaches work together to deliver secure, flexible editing tailored to your application’s architecture.
PRODUCTS
Platform Integrations
End User Applications
Popular Content
RESOURCES
//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.
//---------------------------------------------------------------------------------------
const { PDFNet } = require('@pdftron/pdfnet-node');
const PDFTronLicense = require('../LicenseKey/LicenseKey');
((exports) => {
exports.runElementEditTest = () => {
async function ProcessElements(reader, writer, visited) {
await PDFNet.startDeallocateStack();
const colorspace = await PDFNet.ColorSpace.createDeviceRGB();
const redColor = await PDFNet.ColorPt.init(1, 0, 0);
const blueColor = await PDFNet.ColorPt.init(0, 0, 1);
for (let element = await reader.next(); element !== null; element = await reader.next()) {
const elementType = await element.getType();
let gs;
let formObj;
let formObjNum = null;
switch (elementType) {
case PDFNet.Element.Type.e_image:
case PDFNet.Element.Type.e_inline_image:
// remove all images by skipping them
break;
case PDFNet.Element.Type.e_path:
// Set all paths to red
gs = await element.getGState();
gs.setFillColorSpace(colorspace);
await gs.setFillColorWithColorPt(redColor);
await writer.writeElement(element);
break;
case PDFNet.Element.Type.e_text:
// Set all text to blue
gs = await element.getGState();
gs.setFillColorSpace(colorspace);
await gs.setFillColorWithColorPt(blueColor);
await writer.writeElement(element);
break;
case PDFNet.Element.Type.e_form:
await writer.writeElement(element);
formObj = await element.getXObject();
formObjNum = await formObj.getObjNum();
// if XObject not yet processed
if (visited.indexOf(formObjNum) === -1) {
// Set Replacement
const insertedObj = await formObj.getObjNum();
if (!visited.includes(insertedObj)) {
visited.push(insertedObj);
}
const newWriter = await PDFNet.ElementWriter.create();
await reader.formBegin();
await newWriter.beginOnObj(formObj);
await ProcessElements(reader, newWriter, visited);
await newWriter.end();
await reader.end();
}
break;
default:
await writer.writeElement(element);
}
}
await PDFNet.endDeallocateStack();
}
const main = async () => {
// Relative path to the folder containing test files.
const inputPath = '../TestFiles/';
try {
console.log('Opening the input file...');
const doc = await PDFNet.PDFDoc.createFromFilePath(inputPath + 'newsletter.pdf');
doc.initSecurityHandler();
const writer = await PDFNet.ElementWriter.create();
const reader = await PDFNet.ElementReader.create();
const visited = [];
const itr = await doc.getPageIterator(1);
// Process each page in the document
for (itr; await itr.hasNext(); itr.next()) {
const page = await itr.current();
const sdfObj = await page.getSDFObj();
const insertedObj = await sdfObj.getObjNum();
if (!visited.includes(insertedObj)) {
visited.push(insertedObj);
}
await reader.beginOnPage(page);
await writer.beginOnPage(page, PDFNet.ElementWriter.WriteMode.e_replacement, false, true, await page.getResourceDict());
await ProcessElements(reader, writer, visited);
await writer.end();
await reader.end();
}
await doc.save(inputPath + 'Output/newsletter_edited.pdf', PDFNet.SDFDoc.SaveOptions.e_remove_unused);
console.log('Done. Result saved in newsletter_edited.pdf...');
} catch (err) {
console.log(err);
}
};
PDFNet.runWithCleanup(main, PDFTronLicense.Key).catch(function (error) { console.log('Error: ' + JSON.stringify(error)); }).then(function () { return PDFNet.shutdown(); });
};
exports.runElementEditTest();
})(exports);
// eslint-disable-next-line spaced-comment
//# sourceURL=ElementEditTest.js