The one-line difference
pdfmake is a JavaScript library that takes a declarative document-definition object and renders a PDF in the browser or Node. makesPDF is the same idea, hosted: you POST markdown or a compact DSL (which compiles to a similar declarative tree internally) and get back a PDF/A-2A + PDF/UA-1 compliant document. No bundle to ship, no in-browser font management, real structure tagging, and an agent-native API.
Feature matrix
| pdfmake | makesPDF | |
|---|---|---|
| Shape | JS library (npm) | Hosted REST API (/api/v1/*) |
| Authoring surface | Declarative JS object | Markdown, builder DSL, JSON |
| Runtime | Browser or Node | Cloudflare Workers (we host) |
| Bundle cost | ~1MB (PDFKit + fonts in vfs) | 0 — you call an HTTP endpoint |
| Tagged PDF (PDF/UA-1) | No | Yes, by default |
| PDF/A archival | No | Yes (PDF/A-2A) |
| veraPDF-validated | No | Yes |
| Built-in markdown input | No | Yes (POST /api/v1/md) |
| Determinism | PDFKit-based, mostly stable | Byte-stable across deploys |
| Agent-first | No | Yes — skill file, MCP, x402 |
| First-party CLI | No | Yes (@makespdf/cli) |
| License | MIT | Proprietary hosted; skill file MIT |
Same input, two outputs
A "Hello, Ada" report.
pdfmake — declarative object in your bundle:
import pdfMake from "pdfmake/build/pdfmake";
import pdfFonts from "pdfmake/build/vfs_fonts";
pdfMake.vfs = pdfFonts.pdfMake.vfs;
const docDefinition = {
pageSize: "A4",
pageMargins: 40,
content: [
{ text: "Hello, Ada", fontSize: 24, bold: true, marginBottom: 12 },
{ text: "Welcome to the report." },
],
};
pdfMake.createPdf(docDefinition).download("hello.pdf");
makesPDF — one HTTP call, no bundle:
curl -X POST https://makespdf.com/api/v1/md \
-H "Authorization: Bearer $MAKESPDF_API_KEY" \
-H "Content-Type: application/json" \
-d '{"markdown": "# Hello, Ada\n\nWelcome to the report."}' \
-o hello.pdf
The pdfmake output is untagged — screen readers see one continuous run of text per page. The makesPDF output has a real H1/P structure tree.
Why people switch
- The bundle hurts. pdfmake's vfs ships ~1MB of fonts even if you only use one. Hosted means zero bytes shipped to users.
- Compliance. pdfmake doesn't emit a structure tree; PDF/UA-1 isn't reachable. That blocks ADA Title II tier 1 (US public entities, April 2026) and EAA (June 2025) procurements. We tag by construction and validate against veraPDF.
- Markdown is faster than a document-definition object for content-heavy documents. The declarative shape is fine for invoices; it's slow to write for blog-post-shaped output.
- In-browser fragility. Generating PDFs in the user's browser interacts badly with mobile memory limits, web font loading races, and Safari's ResourceTiming quirks. A server-side API is just a
fetch. - Agents. The pdfmake document-definition object is JSON-emittable but verbose; our DSL is ~75–95% fewer tokens for the same output.
When pdfmake is still the right call
- You must generate the PDF in the user's browser without a network round-trip — offline-first apps, privacy-sensitive workloads where the data can't leave the device.
- You're already shipping pdfmake in production and the migration cost outweighs the operational gains.
- You want a fully MIT-licensed library you can fork and audit end-to-end.
Migration
A pdfmake docDefinition maps cleanly to our builder DSL — content arrays become col(...) / row(...), text blocks become p(...) / h1(...) / s(...), and the same style properties (fontSize, bold, margin) have direct equivalents (font-size, font-weight: bold, margin: [t,r,b,l]). For content-heavy documents, drop the object and use markdown straight against /api/v1/md.