DEV Community

Hichem Bed
Hichem Bed

Posted on

How to Merge PDFs in Node.js (Without a 300-Line Library Setup)

How to Merge PDFs in Node.js (Without a 300-Line Library Setup)

PDF merging in Node.js has a few popular approaches, and they all converge on the same frustrating place: pdf-lib, hummus, or calling out to a Python script wrapping PyPDF2.

These work. But they're not lightweight. pdf-lib is capable, but merging PDFs with complex features — embedded fonts, form fields, digital signatures — can produce broken output. And when it breaks, the error messages are not helpful.

The Setup

npm install renderpdfs
Enter fullscreen mode Exit fullscreen mode

Merging Two PDFs

import RenderPDFs from 'renderpdfs';

const client = new RenderPDFs('rpdf_your_key');

const merged = await client.merge([
  'https://example.com/files/contract.pdf',
  'https://example.com/files/appendix.pdf',
]);

import { writeFileSync } from 'fs';
writeFileSync('combined.pdf', merged);
Enter fullscreen mode Exit fullscreen mode

The input is an array of PDF URLs. The output is a single merged PDF Buffer.

Real Use Case: Bundling a Contract with Its Attachments

app.post('/contracts/:id/bundle', authenticate, async (req, res) => {
  const contract = await db.contracts.findById(req.params.id);
  const attachments = await db.attachments.findByContractId(contract.id);

  const pdfUrls = [contract.pdfUrl, ...attachments.map(a => a.pdfUrl)];

  if (pdfUrls.length < 2) {
    return res.status(400).json({ error: 'Nothing to merge' });
  }

  const merged = await client.merge(pdfUrls);

  res.setHeader('Content-Type', 'application/pdf');
  res.setHeader('Content-Disposition', `attachment; filename="contract-${contract.id}-bundle.pdf"`);
  res.send(merged);
});
Enter fullscreen mode Exit fullscreen mode

Generate + Merge in One Flow

// Step 1: Generate a cover page
const { url: coverUrl } = await client.generate({
  html: `<html><body style="font-family:sans-serif;padding:60px;text-align:center;">
    <h1 style="margin-top:200px;">${title}</h1>
    <p style="color:#555;">${subtitle}</p>
  </body></html>`,
  store: true,
});

// Step 2: Merge cover + body
const packet = await client.merge([coverUrl, existingDocumentUrl]);
res.setHeader('Content-Type', 'application/pdf');
res.send(packet);
Enter fullscreen mode Exit fullscreen mode

Error Handling

try {
  const merged = await client.merge(urls);
} catch (err) {
  if (err.status === 422) {
    return res.status(400).json({ error: 'One or more PDF sources are invalid' });
  }
  throw err;
}
Enter fullscreen mode Exit fullscreen mode

Getting Started

npm install renderpdfs
Enter fullscreen mode Exit fullscreen mode

100 PDFs/month free at renderpdfs.com. API docs at renderpdfs.com/docs.

Top comments (0)