DEV Community

Jalal khan
Jalal khan

Posted on

Split PDF Pages in the Browser with pdf-lib — No Uploads, No Server

A few weeks ago I built a free online Merge PDF tool that runs 100% in the browser. Today I'm sharing its sibling: a Split PDF tool using the same library — pdf-lib — with zero file uploads, zero watermark, and zero server code.

You can try it live here: https://yourutilityhub.com/pdf/split-pdf

Why split PDFs in the browser?

Most online PDF tools upload your file to a server — which means your document is never truly private. Splitting pages locally means:

  • No uploads — nothing leaves your device
  • No watermark or signup
  • Free — no per-page charges
  • Works offline, fast, for files of any size (limited by your browser's memory)

The plan

We'll load the PDF, pick a page range (or specific pages), copy those pages into a fresh PDFDocument, and save the result — all with pdf-lib. Let's walk through the full working component.

1. Install and import

npm install pdf-lib
Enter fullscreen mode Exit fullscreen mode
import { PDFDocument } from "pdf-lib";
Enter fullscreen mode Exit fullscreen mode

2. Load the uploaded file

const arrayBuffer = await file.arrayBuffer();
const pdf = await PDFDocument.load(arrayBuffer);
const totalPages = pdf.getPageCount();
Enter fullscreen mode Exit fullscreen mode

PDFDocument.load() accepts an ArrayBuffer. We read it straight from the File object — no server involved.

3. Split by page range (e.g. 1-5 or 3-)

const parts = pageRange.split("-");
const startRaw = parseInt(parts[0].trim(), 10);
const endRaw = parts[1].trim() === "" ? totalPages : parseInt(parts[1].trim(), 10);

// validate 1..totalPages
const startPage = Math.min(startRaw, endRaw) - 1;  // 0-based
const endPage = Math.max(startRaw, endRaw) - 1;

const newPdf = await PDFDocument.create();
const pageIndices = [];
for (let i = startPage; i <= endPage; i++) {
  pageIndices.push(i);
}
const copiedPages = await newPdf.copyPages(pdf, pageIndices);
copiedPages.forEach(page => newPdf.addPage(page));
Enter fullscreen mode Exit fullscreen mode

The trick: copyPages() wants 0-based indices, but users type 1-based page numbers, so we subtract 1. "3-" with an empty end means "to the last page."

4. Or extract specific pages (e.g. 1,3,5)

const pages = extractPages
  .split(",")
  .map(s => parseInt(s.trim()))
  .filter(n => !isNaN(n) && n > 0 && n <= totalPages);

const newPdf = await PDFDocument.create();
const copiedPages = await newPdf.copyPages(pdf, pages.map(p => p - 1));
copiedPages.forEach(page => newPdf.addPage(page));
Enter fullscreen mode Exit fullscreen mode

We parse comma-separated numbers, filter out anything out of range, then map to 0-based before copying.

5. Save, download, and clean up

const pdfBytes = await newPdf.save();
const blob = new Blob([pdfBytes], { type: "application/pdf" });
const url = URL.createObjectURL(blob);

const link = document.createElement("a");
link.href = url;
link.download = "split_" + file.name;
link.click();
Enter fullscreen mode Exit fullscreen mode

Blob + URL.createObjectURL() lets the user download the split file without the bytes ever touching a server.

Full component

The complete, production-ready React component (mode toggle, validation, error handling, and download) is live here: https://yourutilityhub.com/pdf/split-pdf

The full source is also on GitHub: https://github.com/Jalal-khn/utilityhub-

Caveats worth knowing

  • copyPages reuses the existing page content — it does not re-parse fonts/images per page, so quality is preserved.
  • Very large PDFs are limited by browser memory. For giant files, consider a server-side approach instead.
  • The extracted pages keep their original dimensions and orientation.

Why combine this with Merge PDF?

Many users need both halves of the same workflow — split a big PDF apart, then merge specific pages back into one. Pairing them means users never have to send their documents to a third-party server for either task.

Try it free: https://yourutilityhub.com/pdf/split-pdf

Top comments (0)