Back to projects

HTML to PDF Converter

Built a Next.js web application to allow users to convert HTML to PDF(s) both via the UI and programatically with Vercel infrastructure.

Overview

HTML to PDF Converter is a full-stack SaaS application for converting HTML files into print-ready PDFs. The project supports both a free web interface for one-off conversions and a paid developer API for programmatic PDF generation.

Upload interface

What I Built

The web tool lets users drag and drop up to 10 HTML files, customise page format, orientation, margins, scale, and output mode, then download either a single PDF, a merged PDF, or a ZIP of separate PDFs.

The developer API adds account-based access, JWT API keys, usage tracking, Stripe subscriptions, and rate limiting. Developers can generate an API key from the dashboard and call a REST endpoint to convert HTML files from their own applications.

Developer dashboard

Technical Architecture

The app is built with Next.js App Router, React, TypeScript, MongoDB, NextAuth, Stripe, Upstash Redis, Vercel Blob, and Puppeteer. In development it uses full Puppeteer; in production it uses puppeteer-core with @sparticuz/chromium so Chromium can run inside Vercel's serverless environment.

The conversion flow is:

  1. User uploads one or more HTML files.
  2. The API validates file type, file count, and file size.
  3. Puppeteer renders each HTML document to PDF using the selected options.
  4. Multiple PDFs are either merged with pdf-lib or archived with archiver.
  5. The finished artifact is stored behind a session-based download route.
  6. The UI streams progress updates back to the user using server-sent events.

Sticking Points

Running Chromium on serverless infrastructure

The hardest part was making PDF rendering reliable in production. Local Puppeteer is straightforward, but deploying Chromium in a serverless environment requires a smaller runtime binary and launch flags that work within Vercel's constraints. I split the renderer so local development uses Puppeteer directly while production uses puppeteer-core and @sparticuz/chromium.

Giving users feedback during long conversions

Batch conversion can take several seconds, especially when users upload multiple complex HTML files. Instead of leaving the UI idle, the web route returns a streaming response and sends progress messages such as rendering, merging, archiving, and download preparation.

Supporting batch output cleanly

The app needed to handle three output cases: a normal single PDF, one merged PDF for multiple inputs, or a ZIP containing separate PDFs. I separated the PDF artifact creation logic from the route handler so the rendering, merging, zipping, and download metadata could be tested independently.

Protecting infrastructure costs

Because browser rendering is expensive, the app uses Upstash Redis rate limits for both the free web tool and the paid API. The public web interface is IP-limited, while the API is user-limited and tied to subscription state.

Result

The final product is a working HTML-to-PDF service with a free public converter, a paid API, account management, API key lifecycle controls, Stripe billing, and production-ready serverless rendering. It turns a deceptively simple feature into a complete SaaS workflow: upload, render, download, authenticate, bill, and protect the system from abuse.

The developer documentation is available at htmltopdfconverter.com.au/docs.

API documentation

Stack

Next.jsReactPuppeteer StripeResendMongoDBJWTVercelRedisMaterial UI

Timeline

Feb 2025 — Sep 2025