Minimal PDF creation library. <400 LOC, zero dependencies, makes real PDFs.
npm install tinypdfView sample PDF — Generated with ~50 lines of code
| tinypdf | jsPDF | |
|---|---|---|
| Size | 3.3 KB | 229 KB |
| Dependencies | 0 | 2 |
~70x smaller. We removed TTF fonts, PNG/SVG, HTML-to-PDF, forms, encryption, and compression. What's left is the 95% use case: put text and images on a page.
Invoices, receipts, reports, shipping labels, tickets, certificates, contracts, data exports
| Feature | Description |
|---|---|
| Text | Helvetica, any size, hex colors, align left/center/right |
| Shapes | Rectangles and lines |
| Images | JPEG (photos, logos, signatures) |
| Links | Clickable URLs with optional underline |
| Pages | Multiple pages, custom sizes |
| Markdown | Convert markdown to PDF with headers, lists, rules |
Custom fonts, PNG/GIF/SVG, vector graphics, forms, encryption, compression, HTML-to-PDF
Need those? Use jsPDF or pdf-lib.
import{pdf}from'tinypdf'import{writeFileSync}from'fs'constdoc=pdf()doc.page((ctx)=>{ctx.rect(50,700,200,40,'#2563eb')// blue rectanglectx.text('Hello PDF!',60,712,24,{color: '#ffffff'})ctx.line(50,680,250,680,'#000000',1)// black line})writeFileSync('output.pdf',doc.build())import{readFileSync}from'fs'doc.page((ctx)=>{constlogo=newUint8Array(readFileSync('logo.jpg'))ctx.image(logo,50,700,100,50)})import{pdf,measureText}from'tinypdf'doc.page((ctx)=>{consttext='Visit Example.com'consty=700ctx.text(text,50,y,14,{color: '#0066cc'})ctx.link('https://example.com',50,y-4,measureText(text,14),18,{underline: '#0066cc'})})import{measureText}from'tinypdf'measureText('Hello',12)// => 27.34 (points)import{markdown}from'tinypdf'import{writeFileSync}from'fs'constpdf=markdown(`# Hello WorldA minimal PDF from markdown.## Features- Headers (h1, h2, h3)- Bullet lists- Numbered lists- Horizontal rules---Automatic word wrapping and pagination included.`)writeFileSync('output.pdf',pdf)pdf()// create documentdoc.page(callback)// add page (612×792 default)doc.page(width,height,callback)// add page with custom sizedoc.build()// returns Uint8Arrayctx.text(str,x,y,size,options?)// options:{color, align, width }ctx.rect(x,y,w,h,fill)// filled rectanglectx.line(x1,y1,x2,y2,stroke,width?)// linectx.image(jpegBytes,x,y,w,h)// JPEG imagectx.link(url,x,y,w,h,options?)// options:{underline }measureText(str,size)// text width in pointsmarkdown(str,options?)// options:{width, height, margin }Invoice generator (~50 lines)
import{pdf}from'tinypdf'import{writeFileSync}from'fs'constdoc=pdf()doc.page(612,792,(p)=>{constmargin=40,pw=532// Headerp.rect(margin,716,pw,36,'#2563eb')p.text('INVOICE',55,726,24,{color: '#fff'})p.text('#INV-2025-001',472,728,12,{color: '#fff'})// Company & billing infop.text('Acme Corporation',margin,670,16)p.text('123 Business Street',margin,652,11,{color: '#666'})p.text('New York, NY 10001',margin,638,11,{color: '#666'})p.text('Bill To:',340,670,12,{color: '#666'})p.text('John Smith',340,652,14)p.text('456 Customer Ave',340,636,11,{color: '#666'})p.text('Los Angeles, CA 90001',340,622,11,{color: '#666'})// Tablep.rect(margin,560,pw,25,'#f3f4f6')p.text('Description',50,568,11)p.text('Qty',310,568,11)p.text('Price',380,568,11)p.text('Total',480,568,11)constitems=[['Website Development','1','$5,000.00','$5,000.00'],['Hosting (Annual)','1','$200.00','$200.00'],['Maintenance Package','12','$150.00','$1,800.00'],]lety=535for(const[desc,qty,price,total]ofitems){p.text(desc,50,y,11)p.text(qty,310,y,11)p.text(price,380,y,11)p.text(total,480,y,11)p.line(margin,y-15,margin+pw,y-15,'#e5e7eb',0.5)y-=30}// Totalsp.line(margin,y,margin+pw,y,'#000',1)p.text('Subtotal:',380,y-25,11)p.text('$7,000.00',480,y-25,11)p.text('Tax (8%):',380,y-45,11)p.text('$560.00',480,y-45,11)p.rect(370,y-75,202,25,'#2563eb')p.text('Total Due:',380,y-63,12,{color: '#fff'})p.text('$7,560.00',480,y-63,12,{color: '#fff'})// Footerp.text('Thank you for your business!',margin,80,12,{align: 'center',width: pw,color: '#666'})p.text('Payment due within 30 days',margin,62,10,{align: 'center',width: pw,color: '#999'})})writeFileSync('invoice.pdf',doc.build())MIT
