Working with PDF is something almost every developer needs to know how to do.
Sometimes you need to consolidate reports or invoices, or simply merge multiple documents into one clean file.
Most tools that handle this require either installing software or uploading files to a server, which can be slow and not always ideal – especially when dealing with private documents.
But what if you could integrate PDF directly into the browser without any backend?
Exactly what we will make in this tutorial.
By the end, you’ll have a fully functional browser-based PDF integration. It will allow users to upload files, preview them, rearrange documents using drag and drop, select specific pages and instantly download the final merged PDF.

Table of Contents
How PDF Merging Works in the Browser
At a high level, merging PDFs means loading multiple PDF files, extracting pages from each, and combining them into one document.
Traditionally, this process takes place on the server. Files are uploaded, processed, and then returned to the user.
But modern JavaScript libraries make it possible to do all this directly in the browser. Instead of sending files anywhere, the entire process runs locally on the user’s device.
This approach has some practical advantages. This makes the process faster as there is no upload time involved. It also improves privacy, as the files never leave the user’s system. And from a development perspective, it eliminates the need for back-end processing altogether.
Project setup
We will keep this project simple.
All you need is:
An HTML file
JavaScript
A few libraries
No background needed.
Which library are we using?
We will use two main libraries:
This combination is very powerful and is commonly used in real projects.
Creating an upload interface
Start with a simple drag-and-drop area:
Users can either drag files or click to select.
Once the files are selected, we read them using:
const arrayBuffer = await file.arrayBuffer();
This allows us to move the file to our PDF libraries.
Rendering a PDF preview
To improve usability, we will show a preview of each uploaded PDF.
to use pdf.jswe can render pages like this:
const pdf = await pdfjsLib.getDocument(arrayBuffer).promise;
const page = await pdf.getPage(1);
const viewport = page.getViewport({ scale: 1.5 });
canvas.height = viewport.height;
canvas.width = viewport.width;
page.render({
canvasContext: context,
viewport: viewport
});
It gives users visual feedback before merging.
Rearranging files (drag and drop)
Order matters when merging PDFs.
Instead of forcing users to upload continuously, we will allow resets.
We can use a library like Sortable.js For this:
new Sortable(document.getElementById('pdf-grid'), {
animation: 150
});
It enables drag-and-drop sorting and instant visual updates.
Formatting and Sorting PDFs (Important)
This is where this tool becomes more practical in real-world use.
Instead of forcing users to upload files in a specific order, this tool allows them to rearrange PDFs before merging them.
Users can manually drag and drop files to adjust the order, or use preset sorting options such as sorting files alphabetically or by file size. This makes it easy to quickly organize multiple documents without having to re-upload them.
This flexibility ensures that the final merged document conforms to the layout desired by the user. In real-world scenarios, this is especially useful when combining reports, invoices, or other documents where order is important.
Here's a simple example of how you can organize uploaded files:
function sortFiles(files, type) {
return files.sort((a, b) => {
if (type === "name-asc") {
return a.name.localeCompare(b.name);
}
if (type === "name-desc") {
return b.name.localeCompare(a.name);
}
if (type === "size-asc") {
return a.size - b.size;
}
if (type === "size-desc") {
return b.size - a.size;
}
return 0;
});
}
This allows for precise control over what is merged.
Merging PDFs using JavaScript
Now comes the basic logic. We will use pdf-lib To combine pages:
const { PDFDocument } = PDFLib;
const mergedPdf = await PDFDocument.create();
for (const file of files) {
const pdf = await PDFDocument.load(file.arrayBuffer);
const pages = await mergedPdf.copyPages(pdf, selectedPages);
pages.forEach(page => mergedPdf.addPage(page));
}
const pdfBytes = await mergedPdf.save();
Finally, we'll create a downloadable file:
const blob = new Blob((pdfBytes), { type: 'application/pdf' });
Improving user experience
A simple integration tool works, but a good tool feels streamlined.
Small improvements make a big difference.
For example:
-
Showing a preview before merging.
-
Allows users to remove files.
-
Enabling page navigation
-
Providing instant feedback
These details turn a basic feature into a real product.
Demo: How PDF Merge Works
Here's what the full flow looks like in practice:
Step 1: Upload the PDF.

Users can drag and drop PDF files into the upload area or select them manually.
Step 2: Review the files.

Each uploaded file is displayed with a preview as well as PDF file details (name, size, page count, and so on), so users can verify the content before merging.
Step 3: Rearrange the files.

Users can sort PDFs using drag and drop or sorting options as well as manual options. This ensures that the merged final document follows the correct sequence.
Step 4: Merge the PDF

Once everything is set, users can click the Merge button to combine all the selected PDFs into one file.
Step 5: Download the final PDF.

The merged PDF is generated instantly in the browser, and users can preview, rename, and download it without any server interaction.
Important notes from real-world usage
When building tools like PDF Merger, handling large files efficiently becomes important.
If several large PDFs are loaded at the same time, it can slow down the browser or use up a lot of memory. Rather than processing everything at once, it's better to handle files in stages.
For example, instead of loading all PDFs at once, you can process them one by one:
const { PDFDocument } = PDFLib;
const mergedPdf = await PDFDocument.create();
for (const file of files) {
const arrayBuffer = await file.arrayBuffer();
const pdf = await PDFDocument.load(arrayBuffer);
const pages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
pages.forEach(page => mergedPdf.addPage(page));
}
This approach keeps memory usage low and avoids freezing the browser when working with large files.
You can also optimize by limiting the file size or the number of files that users can upload at once. This helps keep the tool responsive even on low-powered devices.
Another important aspect is privacy. Because everything runs directly in the browser, files are never uploaded to the server. This means that sensitive documents remain on the user's device.
But it's still important to be transparent about it. In real-world tools, you should clearly state that all processing takes place locally and no files are saved or transferred.
This client-side approach improves both performance and user confidence, especially when working with private or confidential documents.
Common Mistakes to Avoid
A common mistake is to skip validation. The integration process may fail if users upload invalid files or empty input.
Another problem is ignoring page boundaries. If the analysis is incorrect, users may get unexpected results.
Also, relying on fixed layouts or assumptions can break the experience across different files. Testing with different PDF types is necessary.
The result
In this tutorial, you created a browser-based PDF integration using JavaScript.
More importantly, you learned how to process files natively in the browser, render previews for better usability, handle user input safely, and manage dynamic document structures when working with PDFs.
This approach removes the need for a backend and keeps everything fast, private and efficient.
Once you understand this pattern, you can extend it to create more advanced tools. For example, you can create features like PDF splitting, compression, editing, or other document-based utilities using the same basic ideas.
And this is where things start to get really interesting.