npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

tinypdf

v0.4.1

Published

Minimal PDF creation library. <400 LOC, zero dependencies, makes real PDFs.

Readme

tinypdf

npm size license

Minimal PDF creation library. <400 LOC, zero dependencies, makes real PDFs.

npm install tinypdf

| Executive report | Event ticket | |---|---| | Executive report | Event ticket | | KPI dashboard and chart | Bold landscape admission ticket | | Certificate | Invoice | | Certificate | Invoice | | Formal award certificate | Editorial studio invoice |

Run any example with Bun, for example: bun examples/executive-report.ts.


Why tinypdf?

| | 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.

Build with it

Invoices, receipts, reports, shipping labels, tickets, certificates, contracts, data exports

Features

| Feature | Description | |---------|-------------| | Text | Helvetica (WinAnsi), 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 |

Not included

Unicode/custom fonts, PNG/GIF/SVG, vector graphics, forms, encryption, compression, HTML-to-PDF

Need those? Use jsPDF or pdf-lib.


Quick start

import { pdf } from 'tinypdf'
import { writeFileSync } from 'fs'

const doc = pdf()

doc.page((ctx) => {
  ctx.rect(50, 700, 200, 40, '#2563eb')           // blue rectangle
  ctx.text('Hello PDF!', 60, 712, 24, { color: '#ffffff' })
  ctx.line(50, 680, 250, 680, '#000000', 1)       // black line
})

writeFileSync('output.pdf', doc.build())

Add images

import { readFileSync } from 'fs'

doc.page((ctx) => {
  const logo = new Uint8Array(readFileSync('logo.jpg'))
  ctx.image(logo, 50, 700, 100, 50)
})

Add links

import { pdf, measureText } from 'tinypdf'

doc.page((ctx) => {
  const text = 'Visit Example.com'
  const y = 700
  ctx.text(text, 50, y, 14, { color: '#0066cc' })
  ctx.link('https://example.com', 50, y - 4, measureText(text, 14), 18, { underline: '#0066cc' })
})

Measure text width

import { measureText } from 'tinypdf'

measureText('Hello', 12) // => 27.34 (points)

Markdown to PDF

import { markdown } from 'tinypdf'
import { writeFileSync } from 'fs'

const pdf = markdown(`
# Hello World

A 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)

Stream large PDFs

Use buildStream() to emit a ReadableStream<Uint8Array> incrementally. Page content and image inputs are retained until the stream reaches them, but stream bodies are emitted directly and the final PDF is not assembled into one additional full-size buffer.

import { pdf } from 'tinypdf'

const doc = pdf()
for (let i = 0; i < 10_000; i++) {
  doc.page((ctx) => ctx.text(`Page ${i + 1}`, 50, 750, 12))
}

// Write to disk (Bun)
await Bun.write('huge.pdf', doc.buildStream())

// Or serve as an HTTP response
return new Response(doc.buildStream(), {
  headers: { 'Content-Type': 'application/pdf' },
})

API

pdf()                                      // create document
doc.page(callback)                         // add page (612×792 default)
doc.page(width, height, callback)          // add page with custom size
doc.build()                                // returns Uint8Array
doc.buildStream()                          // returns ReadableStream<Uint8Array>

ctx.text(str, x, y, size, options?)        // options: { color, align, width }
ctx.rect(x, y, w, h, fill)                 // filled rectangle
ctx.line(x1, y1, x2, y2, stroke, width?)   // line
ctx.image(jpegBytes, x, y, w, h)           // JPEG image
ctx.link(url, x, y, w, h, options?)        // options: { underline }

measureText(str, size)                     // text width in points
markdown(str, options?)                    // options: { width, height, margin }

Full example

See the complete invoice generator for a production-style layout using only tinypdf's core text, rectangle, and line primitives.


License

MIT