Merge PDFs in the browser with JavaScript (no uploads, no server)

In this post I’ll show how to merge PDF files entirely in the browser using PDF.js and pdf-lib — no server, no file upload, no backend. Everything runs on the user’s machine, which is great for privacy and for keeping hosting costs at zero (it’s just a static site).

Why process PDFs on the client?

Most “free” PDF websites quietly upload your documents to their server, which:

  • Exposes private/sensitive files to third parties
  • Imposes size limits
  • Often slaps a watermark on the output
  • Requires you to trust their storage

If you handle PDFs with client-side JavaScript (WebAssembly / WASM + PDF.js), none of that happens. The user’s file never leaves their device, and you don’t need a backend at all — so it’s cheap and private.

Caveats

  • pdf-lib works well with standard PDFs; heavily encrypted or unusual documents may need extra handling.
  • Very large PDFs are memory-hungry since everything is client-side, but for typical documents it’s fast and free.
  • Some complex PDFs with unusual fonts can lose fidelity — test on your own files first.

Try it

I packaged this approach (plus split, compress, rotate, unlock, image-to-PDF) into a free no-upload tool: https://yourutilityhub.com/pdf/merge-pdf

The whole project is open source: https://github.com/Jalal-khn/utilityhub-

If you have questions about the architecture or want a deeper dive on any part, ask away.

The basic idea

  1. Read the input file with FileReader
  2. Parse it with pdf-lib (a pure-JS PDF library)
  3. Copy the source pages into a new document
  4. Save the merged PDF and trigger a download

Here’s the core function:


js
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 out = await merged.save();
  return new Blob([out], { type: "application/pdf" });
}
Total
0
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post

The Pentagon now has its own version of ChatGPT and Grok

Next Post

Preparedness in Quality Manufacturing

Related Posts