Building browser-only PDF tools — merge, split, and canvas builder without touching a server

Published: (March 23, 2026 at 06:28 AM EDT)
3 min read
Source: Dev.to

Source: Dev.to

Most PDF processing tools upload your files to a server, but modern browser APIs let you handle PDFs entirely client‑side. Below is a concise guide to building three browser‑only PDF tools—merger, splitter, and canvas builder—using pdf‑lib, pdfjs‑dist, jszip, and Fabric.js.

PDF Merging with pdf-lib

pdf-lib works in both the browser and Node and provides a clean API for merging, splitting, and manipulating pages.

import { PDFDocument } from "pdf-lib";

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

  for (const file of files) {
    const src = await PDFDocument.load(file.bytes);
    const indices = parseRanges(file.ranges, src.getPageCount());
    const copied = await merged.copyPages(src, indices);
    copied.forEach(page => merged.addPage(page));
  }

  return await merged.save(); // Uint8Array
}

Page‑range selection

Users can specify ranges like "1-3,5,7-9" to pick specific pages from each file before merging.

function parseRanges(input: string, pageCount: number): number[] {
  if (!input.trim()) return Array.from({ length: pageCount }, (_, i) => i);
  const pages = new Set();
  const parts = input.split(",").map(s => s.trim());

  for (const part of parts) {
    if (part.includes("-")) {
      const [a, b] = part.split("-").map(Number);
      for (let i = a; i = 1 && n  a - b);
}

Generating Thumbnails with pdfjs-dist

pdfjs-dist (Mozilla’s PDF renderer) can render pages to a canvas, which you can then turn into image thumbnails.

const pdfjsLib = await import("pdfjs-dist");
pdfjsLib.GlobalWorkerOptions.workerSrc =
  `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjsLib.version}/pdf.worker.min.mjs`;

const pdf = await pdfjsLib.getDocument({ data: new Uint8Array(bytes) }).promise;
const page = await pdf.getPage(1);
const viewport = page.getViewport({ scale: 0.3 });

const canvas = document.createElement("canvas");
canvas.width = viewport.width;
canvas.height = viewport.height;

await page.render({
  canvasContext: canvas.getContext("2d"),
  viewport,
}).promise;

const thumb = canvas.toDataURL("image/jpeg", 0.7);

Packaging Split PDFs with jszip

When splitting a PDF into multiple files, jszip lets you bundle the results into a single ZIP download.

import JSZip from "jszip";

async function zipSplitPages(src, pageCount) {
  const zip = new JSZip();

  for (let i = 0; i  import("@/components/pdf/PdfBuilderClient"),
  { ssr: false }
);

Apply the same pattern to any component that relies on browser‑only libraries (e.g., the merger or splitter).

Privacy Benefits of Browser‑Only Processing

When all work happens in the browser, no data ever leaves the user’s device. This eliminates accidental leaks of sensitive documents such as:

  • Contracts & NDAs
  • Financial statements
  • Medical records
  • Personal identification

Open DevTools → Network tab while using the tools: you’ll see no upload requests—everything stays local.

Available Tools

  • PDF Merger – combine PDFs with per‑file page‑range selection.
  • PDF Splitter – split by pages, custom ranges, or visual selection.
  • PDF Canvas Builder – design PDFs from scratch with a contextual Fabric.js UI.

If you’re building browser‑based file‑processing utilities, the pdf-lib + pdfjs-dist combo (plus jszip and Fabric.js when needed) covers the majority of PDF workflows without any server infrastructure.

0 views
Back to Blog

Related posts

Read more »