Dev.to WebDev 🛠 Dev 👁 0 📖 3 min read

Merging PDFs without uploading them: how pdf-lib runs entirely in your browser

Merging PDFs without uploading them: how pdf-lib runs entirely in your browser Yesterday I answered a question about merging PDFs for free without a watermark, and the most common follow-up was: "free, sure — but where

Merging PDFs without uploading them: how pdf-lib runs entirely in your browser

Yesterday I answered a question about merging PDFs for free without a watermark, and the most common follow-up was: "free, sure — but where does my file go?" It's a fair question. Most free PDF tools upload your document to a server, merge it there, and hand it back. Your file — a contract, a payslip, a visa application — sits on infrastructure you don't control, for as long as their retention policy says.

There is a different way to build the same tool, and it's not even hard. The library is pdf-lib, it runs entirely in the browser, and the whole merge is about thirty lines of code. Here's how it works, what it can't do, and why I built ToolNest's PDF tools this way.

The idea in one paragraph

A PDF is a structured document, not a picture. pdf-lib parses that structure in JavaScript, lets you copy pages from several documents into one new document, and serialises the result back to bytes — all in memory, in your tab. No upload happens because there is no server endpoint to upload to. The browser does the work; the file never leaves the machine.

The code

The core is small enough to read in full:

import { PDFDocument } from 'pdf-lib';

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

  for (const file of files) {
    const bytes = await file.arrayBuffer();
    const src = await PDFDocument.load(bytes, {
      ignoreEncryption: true,
    });
    const pages = await merged.copyPages(
      src,
      src.getPageIndices()
    );
    pages.forEach((page) => merged.addPage(page));
  }

  const mergedBytes = await merged.save();
  return new Blob([mergedBytes], { type: 'application/pdf' });
}

Wire that to a file input and a download link and you have a working merger:

input.addEventListener('change', async () => {
  const blob = await mergePdfs([...input.files]);
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'merged.pdf';
  a.click();
  URL.revokeObjectURL(url);
});

That's the whole product. Everything else — drag to reorder pages, a page-range picker, a progress indicator — is interface.

What it handles well

  • Ordinary merges. Two or three PDFs into one, in the order you choose. This covers the vast majority of real use: application packs, scanned forms, a contract plus its annexes.
  • Page ranges. copyPages takes an index list, so "pages 2–4 of this file, all of that one" is one line, not a separate feature.
  • Form fields and links. Copied pages keep their annotations. A merged contract still has its clickable links, which is the detail that separates a real merge from screenshotting pages into a new file.

The honest limits

  • Encrypted PDFs. A password-protected file needs its password before pdf-lib can open it. ignoreEncryption: true lets the load proceed in some cases, but a file that genuinely requires a password will fail until you supply it — and the password, like the file, is typed into your own browser, not sent anywhere.
  • Very large files. Everything happens in memory. Merging a dozen 100MB scans on a phone will be slow and may run out of memory. For that scale, a local desktop tool is the right answer.
  • Scanned image PDFs. pdf-lib copies pages; it doesn't OCR them. A scanned document merges fine, but it stays a picture of text until you run OCR separately.

Why this matters beyond PDFs

The same pattern — a capable library, WebAssembly or plain JavaScript, everything local — is how ToolNest's background remover, image converter and file tools work too. The architecture choice is the product: when a tool runs on your machine, it can't change price, change terms, or close on three weeks' notice. remove.bg's standalone site closes on 1 December 2026; a browser-based tool has no standalone site to close.

There is a trade-off, and it's worth naming. A server can throw a GPU at a hard problem; a browser has whatever your device has. For merging PDFs the browser is more than enough, which is why this is one of the easiest tools to build honestly: free, no account, no upload, no watermark, no retention policy to read.

If you want to try the finished version rather than build it, ToolNest's PDF merger is at https://toolnest-xi-silk.vercel.app/tools/pdf-merger/ — open it, drop in your files, download the result. And if you build your own, start with the thirty lines above. They're the whole trick.

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.