---
title: "How to manipulate PDF pages with pdf-lib: Extract, merge, and remove pages"
canonical_url: "https://www.nutrient.io/blog/pdfjs-pdf-page-manipulation-pdf-lib/"
md_url: "https://www.nutrient.io/blog/pdfjs-pdf-page-manipulation-pdf-lib.md"
last_updated: "2026-07-14T18:00:17.434Z"
description: "Learn how to use pdf-lib for PDF page manipulation in Node.js. This tutorial covers extracting pages, merging multiple PDFs, removing specific pages, and trimming documents for processing."
---

**TL;DR**

PDF.js reads and renders PDFs but can’t modify them structurally. Reach for `pdf-lib` when you need to:

- Extract specific pages from a source PDF.

- Merge multiple PDFs into one.

- Remove pages by index.

- Trim a PDF to the first N pages before uploading to a processing service.

The core API is `PDFDocument.load()` → `copyPages()` → `addPage()` → `save()`. Page indices are zero-based, and `pdf-lib` works in both Node.js and the browser with no external workers or fonts. [Nutrient Web SDK](https://www.nutrient.io/sdk/web-overview/) handles the same operations client-side via the `applyOperations` API.

PDF.js reads and renders PDFs but doesn’t modify them structurally. For operations like extracting pages, merging documents, or trimming PDFs, use `pdf-lib`.

## Install

Install `pdf-lib` with npm:

```bash

npm install pdf-lib

```

## Copying specific pages

Extract the first N pages from a PDF — useful for sending a preview to a processing service:

```tsx

import { PDFDocument } from "pdf-lib";

async function copyFirstTwoPages(pdfBuffer) {
  const originalPdf = await PDFDocument.load(pdfBuffer);
  const pageCount = originalPdf.getPageCount();

  if (pageCount === 0) {
    throw new Error("The PDF has no pages.");
  }

  const newPdf = await PDFDocument.create();
  const indices = originalPdf.getPageIndices().slice(0, 2);
  const pages = await newPdf.copyPages(originalPdf, indices);
  pages.forEach((page) => newPdf.addPage(page));

  return Buffer.from(await newPdf.save());
}

```

## Key concepts

The core `PDFDocument` API covers three building blocks: loading, creating, and copying pages.

### Loading a PDF

`PDFDocument.load()` accepts a `Buffer`, `Uint8Array`, or base64 string and returns a `PDFDocument` instance:

```tsx

// From Buffer.
const pdf = await PDFDocument.load(buffer);

// From Uint8Array.
const pdf = await PDFDocument.load(uint8Array);

// From base64 string.
const pdf = await PDFDocument.load(base64String);

```

### Creating a new PDF

Use `PDFDocument.create()` to start a new, empty document that you can populate with pages copied from existing PDFs:

```tsx

const newPdf = await PDFDocument.create();

```

### Copying pages between documents

`copyPages` copies pages from a source document into a destination document. It returns page objects that you then add:

```tsx

// Copy pages at indices 0, 2, 4 from source.
const copiedPages = await destPdf.copyPages(sourcePdf, [0, 2, 4]);

// Add them to the destination.
for (const page of copiedPages) {
  destPdf.addPage(page);
}

```

### Saving

`save()` serializes the document back to bytes. The return value is a `Uint8Array`, which you can convert to a Node.js `Buffer` for writing to disk, passing to an HTTP response, or uploading to storage:

```tsx

const bytes = await pdf.save(); // Returns Uint8Array.
const buffer = Buffer.from(bytes); // Convert to Node.js Buffer.

```

## Use case: Trim a PDF before processing

Some services (like GROBID for academic metadata extraction) only need the first few pages. Sending a trimmed PDF is faster and avoids upload size limits:

```tsx

async function trimForProcessing(pdfBuffer, maxPages = 2) {
  const original = await PDFDocument.load(pdfBuffer);
  const pageCount = original.getPageCount();
  const pagesToCopy = Math.min(pageCount, maxPages);

  const trimmed = await PDFDocument.create();
  const indices = Array.from({ length: pagesToCopy }, (_, i) => i);
  const pages = await trimmed.copyPages(original, indices);

  for (const page of pages) {
    trimmed.addPage(page);
  }

  return Buffer.from(await trimmed.save());
}

```

## Use case: Merge multiple PDFs

Loop over an array of buffers, copying all pages from each source into a single output document:

```tsx

async function mergePdfs(pdfBuffers) {
  const merged = await PDFDocument.create();

  for (const buffer of pdfBuffers) {
    const source = await PDFDocument.load(buffer);
    const indices = source.getPageIndices(); // [0, 1, 2,...]
    const pages = await merged.copyPages(source, indices);

    for (const page of pages) {
      merged.addPage(page);
    }
  }

  return Buffer.from(await merged.save());
}

```

## Use case: Remove specific pages

Filter the page indices to exclude the ones you want to remove. Then copy the rest into a new document:

```tsx

async function removePages(pdfBuffer, pagesToRemove) {
  const original = await PDFDocument.load(pdfBuffer);
  const allIndices = original.getPageIndices();
  const keepIndices = allIndices.filter((i) =>!pagesToRemove.includes(i));

  const result = await PDFDocument.create();
  const pages = await result.copyPages(original, keepIndices);

  for (const page of pages) {
    result.addPage(page);
  }

  return Buffer.from(await result.save());
}

```

## pdf-lib vs. PDF.js

| Operation           | pdf-lib | PDF.js                    |
| ------------------- | ------- | ------------------------- |
| Read text content   | No      | Yes (`getTextContent`)    |
| Render pages        | No      | Yes (`PDFViewer`, canvas) |
| Copy/move pages     | Yes     | No                        |
| Create new PDFs     | Yes     | No                        |
| Modify PDF metadata | Yes     | No                        |
| Add form fields     | Yes     | No                        |
| Embed images/fonts  | Yes     | No                        |

Use PDF.js for reading and rendering. Use pdf-lib for creating and modifying.

## Key points

- `pdf-lib` works in both Node.js and the browser.

- `copyPages` is the core method — it copies pages between documents.

- Always call `addPage` after `copyPages` — copied pages aren’t automatically added.

- `save()` returns a `Uint8Array`; convert with `Buffer.from()` for Node.js.

- Page indices are zero-based.

- `pdf-lib` doesn’t need any external fonts, workers, or CMaps.

## FAQ

#### Why can’t <code>PDF.js</code> modify PDFs?

PDF.js is a parser and renderer, not a writer. It opens and displays PDFs but has no API to create, modify, or save them. For structural changes — adding, removing, reordering, or merging pages — you need a library like `pdf-lib` or `pdfkit`, or a commercial SDK with editing support.

#### Does <code>pdf-lib</code> work in the browser?

Yes. `pdf-lib` is pure JavaScript with no native dependencies, so it runs in Node.js and the browser. In the browser, load PDFs from a `File`, `Blob`, or `Uint8Array` instead of a `Buffer`, and use `Blob`/`URL.createObjectURL` to deliver the saved output for download.

#### Why do I need <code>addPage</code> after <code>copyPages</code>?

`copyPages` returns an array of `PDFPage` objects that have been deep-copied from the source document into the destination, but they aren’t inserted into the destination’s page tree until you call `addPage` (or `insertPage`). This two-step design lets you copy pages once and reorder them freely before committing.

#### How do I convert <code>pdf-lib</code>’s output to a Node.js <code>Buffer</code>?

`pdfDocument.save()` returns a `Uint8Array`. Wrap it with `Buffer.from()` to get a Node.js `Buffer` for `fs.writeFile`, HTTP responses, or S3 uploads: `const buffer = Buffer.from(await pdf.save())`.

#### Can <code>pdf-lib</code> render or display PDFs?

No. `pdf-lib` builds and modifies PDFs but doesn’t render them. Pair it with PDF.js (for viewing) or a commercial viewer SDK when you need both editing and display.

#### How does Nutrient Web SDK compare?

Nutrient handles add, remove, merge, split, rotate, and reorder operations client-side through `instance.applyOperations()` and exports the result with `instance.exportPDF()`. There’s no separate library to install, no manual `copyPages` and `addPage` loops, and the same operations apply to a live viewer. See the [page manipulation](https://www.nutrient.io/guides/web/editor/add-page.md) guide for details.

## How Nutrient Web SDK handles this

Nutrient Web SDK handles page manipulation directly in the browser with the `applyOperations` API — no `pdf-lib` dependency, no manual page copying, and no `addPage()` loops:

```js

// Add a blank page after page 1.
instance.applyOperations([
  {
    type: "addPage",
    afterPageIndex: 1,
    backgroundColor: new NutrientViewer.Color({ r: 255, g: 255, b: 255 }),
    pageWidth: 595,
    pageHeight: 842,
  },
]);

// Remove a page.
instance.applyOperations([{ type: "removePage", pageIndex: 3 }]);

// Export the modified PDF.
const buffer = await instance.exportPDF();

```

Nutrient also supports merging, splitting, rotating, cropping, and reordering — all client-side.

---

_See [Nutrient Web SDK](https://www.nutrient.io/sdk/web-overview/) for an alternative to PDF.js and `pdf-lib`. The [page manipulation](https://www.nutrient.io/guides/web/editor/add-page.md) and [save/export](https://www.nutrient.io/guides/web/save-a-document/to-arraybuffer.md) guides cover the API in depth. Follow the [migration guide](https://www.nutrient.io/guides/web/about/migration-guides/migrating-from-mozilla-pdfjs.md) to switch, or [talk to Sales](https://www.nutrient.io/contact-sales/?=sdk) about your requirements._
---

## Related pages

- [The business case for accessibility: Five ways it drives enterprise value](/blog/5-ways-accessibility-drives-enterprise-value.md)
- [Accessibility Untangled Why It Matters Guide](/blog/accessibility-untangled-why-it-matters-guide.md)
- [Advanced Techniques For React Native Ui Components](/blog/advanced-techniques-for-react-native-ui-components.md)
- [Ai Document Automation Extraction To Action](/blog/ai-document-automation-extraction-to-action.md)
- [Ai Legal Assistant Document Authoring](/blog/ai-legal-assistant-document-authoring.md)
- [Angular File Viewer Pdf Image Office Files](/blog/angular-file-viewer-pdf-image-office-files.md)
- [Auto Tagging And Document Accessibility In Dotnet Sdk](/blog/auto-tagging-and-document-accessibility-in-dotnet-sdk.md)
- [Best Document Viewers](/blog/best-document-viewers.md)
- [The CEO’s AI playbook: Why decision architecture beats model selection](/blog/ceo-ai-playbook-decision-architecture.md)
- [Complete Guide To Pdfjs](/blog/complete-guide-to-pdfjs.md)
- [Construction Document Data Extraction](/blog/construction-document-data-extraction.md)
- [Convert One Drive Files To Pdf In Sharepoint](/blog/convert-one-drive-files-to-pdf-in-sharepoint.md)
- [Create And Edit Pdfs In Flutter](/blog/create-and-edit-pdfs-in-flutter.md)
- [Create Pdfs With React](/blog/create-pdfs-with-react.md)
- [Creating A Document Scanner With Ocr In Python](/blog/creating-a-document-scanner-with-ocr-in-python.md)
- [The CTO’s AI playbook: Why accountability architecture beats orchestration](/blog/cto-ai-playbook-accountability-architecture.md)
- [Digital Signatures](/blog/digital-signatures.md)
- [Digital Workflow Automation](/blog/digital-workflow-automation.md)
- [Document Ai Vs Ocr](/blog/document-ai-vs-ocr.md)
- [Document Viewer](/blog/document-viewer.md)
- [Emerging threats: Your logging system may be an agentic threat vector](/blog/emerging-threats-your-logging-system.md)
- [app.py](/blog/extract-text-from-pdf-using-python.md)
- [How To Build A Dotnet Maui Pdf Viewer](/blog/how-to-build-a-dotnet-maui-pdf-viewer.md)
- [How To Build A Flutter Pdf Viewer](/blog/how-to-build-a-flutter-pdf-viewer.md)
- [or](/blog/how-to-build-a-javascript-pdf-viewer-with-pdfjs.md)
- [How To Build A Javascript Pdf Viewer](/blog/how-to-build-a-javascript-pdf-viewer.md)
- [or](/blog/how-to-build-a-nextjs-pdf-viewer.md)
- [How To Build A Powerpoint Viewer Using Javascript](/blog/how-to-build-a-powerpoint-viewer-using-javascript.md)
- [Using Yarn](/blog/how-to-build-a-react-excel-viewer.md)
- [How To Build A React Native Pdf Viewer](/blog/how-to-build-a-react-native-pdf-viewer.md)
- [How To Build A React Powerpoint Viewer](/blog/how-to-build-a-react-powerpoint-viewer.md)
- [How To Build A Reactjs File Viewer](/blog/how-to-build-a-reactjs-file-viewer.md)
- [or](/blog/how-to-build-a-reactjs-pdf-viewer-with-react-pdf.md)
- [or](/blog/how-to-build-a-reactjs-pdf-viewer.md)
- [How To Build A Reactjs Viewer With Pdfjs](/blog/how-to-build-a-reactjs-viewer-with-pdfjs.md)
- [How To Build A Vuejs Pdf Viewer With Pdfjs](/blog/how-to-build-a-vuejs-pdf-viewer-with-pdfjs.md)
- [How To Build A Vuejs Pdf Viewer](/blog/how-to-build-a-vuejs-pdf-viewer.md)
- [How To Build An Android Pdf Viewer](/blog/how-to-build-an-android-pdf-viewer.md)
- [How To Build An Angular Pdf Viewer With Ng2 Pdf Viewer](/blog/how-to-build-an-angular-pdf-viewer-with-ng2-pdf-viewer.md)
- [How To Build An Angular Pdf Viewer With Pdfjs](/blog/how-to-build-an-angular-pdf-viewer-with-pdfjs.md)
- [How To Convert Html To Pdf Using Html2pdf](/blog/how-to-convert-html-to-pdf-using-html2pdf.md)
- [or](/blog/how-to-convert-html-to-pdf-using-react.md)
- [or](/blog/how-to-convert-html-to-pdf-using-wkhtmltopdf-and-python.md)
- [How To Convert Word To Pdf In Nodejs](/blog/how-to-convert-word-to-pdf-in-nodejs.md)
- [How To Create Pdfs With React To Pdf](/blog/how-to-create-pdfs-with-react-to-pdf.md)
- [How To Edit Pdfs Using Ios Pdf Library](/blog/how-to-edit-pdfs-using-ios-pdf-library.md)
- [How To Embed A Pdf Viewer In Your Website](/blog/how-to-embed-a-pdf-viewer-in-your-website.md)
- [How To Generate Pdf From Html With Nodejs](/blog/how-to-generate-pdf-from-html-with-nodejs.md)
- [base_url tells WeasyPrint where to resolve relative asset paths](/blog/how-to-generate-pdf-reports-from-html-in-python.md)
- [Open an image.](/blog/how-to-use-tesseract-ocr-in-python.md)
- [From an HTML string.](/blog/html-in-pdf-format.md)
- [Linearized Pdf](/blog/linearized-pdf.md)
- [Swift Package Manager](/blog/mobile-pdf-sdk.md)
- [`elements` come from your document parser — each has a type and content.](/blog/multimodal-rag.md)
- [Nutrient Vs Conga Composer](/blog/nutrient-vs-conga-composer.md)
- [Online Document Viewer](/blog/online-document-viewer.md)
- [Open Pdf In Your Web App](/blog/open-pdf-in-your-web-app.md)
- [Pdf Page Labels](/blog/pdf-page-labels.md)
- [Pdf Sdk Compliance Security Checklist](/blog/pdf-sdk-compliance-security-checklist.md)
- [Pdf Sdk Performance Benchmark](/blog/pdf-sdk-performance-benchmark.md)
- [Pdf Ua Compliance Guide](/blog/pdf-ua-compliance-guide.md)
- [Pdfjs Annotation Editor Layer](/blog/pdfjs-annotation-editor-layer.md)
- [Pdfjs Area Annotations Canvas Capture](/blog/pdfjs-area-annotations-canvas-capture.md)
- [Pdfjs Coordinate Systems Pdf To Screen](/blog/pdfjs-coordinate-systems-pdf-to-screen.md)
- [Pdfjs Eventbus Guide](/blog/pdfjs-eventbus-guide.md)
- [Pdfjs Limitations Commercial Upgrade](/blog/pdfjs-limitations-commercial-upgrade.md)
- [Pdfjs Native Annotation Layer Forms](/blog/pdfjs-native-annotation-layer-forms.md)
- [Pdfjs Navigation Zoom Rotation](/blog/pdfjs-navigation-zoom-rotation.md)
- [Pdfjs React Viewer Setup](/blog/pdfjs-react-viewer-setup.md)
- [Pdfjs Rendering Overlays React Portals](/blog/pdfjs-rendering-overlays-react-portals.md)
- [Pdfjs Server Side Text Extraction](/blog/pdfjs-server-side-text-extraction.md)
- [Pdfjs Sticky Note Annotations](/blog/pdfjs-sticky-note-annotations.md)
- [Pdfjs Text Highlight Annotations](/blog/pdfjs-text-highlight-annotations.md)
- [Pdfjs Text Search Pdffindcontroller](/blog/pdfjs-text-search-pdffindcontroller.md)
- [Pdfjs Thumbnail Sidebar](/blog/pdfjs-thumbnail-sidebar.md)
- [Process Flows](/blog/process-flows.md)
- [React Native Pdf Annotation](/blog/react-native-pdf-annotation.md)
- [or](/blog/sample-blog-updated.md)
- [Open an image file.](/blog/tesseract-python-guide.md)
- [Top 5 Javascript Pdf Viewers](/blog/top-5-javascript-pdf-viewers.md)
- [Convert an HTML file to PDF.](/blog/top-ten-ways-to-convert-html-to-pdf.md)
- [Vector Pdf](/blog/vector-pdf.md)
- [Wcag2 Accessibility Requirements Documents](/blog/wcag2-accessibility-requirements-documents.md)
- [Web Sdk Is Now Headless](/blog/web-sdk-is-now-headless.md)
- [What Are Annotations](/blog/what-are-annotations.md)
- [What Is A Vpat](/blog/what-is-a-vpat.md)
- [What Is Pdf Ua](/blog/what-is-pdf-ua.md)
- [Why Pdfium Is A Trusted Platform For Pdf Rendering](/blog/why-pdfium-is-a-trusted-platform-for-pdf-rendering.md)
- [Why Your Ai Agent Hallucinates Pdf Table Data](/blog/why-your-ai-agent-hallucinates-pdf-table-data.md)

