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.
copyPagestakes 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: truelets 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.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.