pre-release · v1.0.0-beta.2

@jasy/pdf · declarative pdf engine · pure typescript

Create PDFs easier than ever.

A declarative, component-based PDF engine in pure TypeScript. Write components, get paper. ZUGFeRD & XRechnung compliant, with no headless browser and no Java underneath.

Get started GitHub
invoice.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
renders to
  • no chromium
  • no jvm
  • AFM + TrueType
  • real pagination
  • EN-16931
  • MIT

one engine · two runtimes · same code

Runs on your server. And in the browser.

Pure TypeScript, no Node-only crutches. The same component tree produces the same byte-exact PDF whether it runs on a server or entirely in the browser - your call, not the library's.

on your server

Node, Nitro, Lambda, Workers

Generate invoices and reports in your API or backend - dependency-light, no Chromium to boot, no JVM to feed.

in the browser

100% client-side

Render the whole PDF in the user's browser - no server round-trip, works offline, and nothing ever leaves the device.

$const bytes = await renderToBytes(doc) // same call, server or browser

@jasy/vue · PDFs as Vue components

new · on npm

Your PDF is a Vue component .

Author a document the way you build an app - components, props, slots and your reactive data - then render it to a real PDF, right in the browser. Real layout and pagination underneath, fully typed props, and a familiar feel if you have reached for @react-pdf/renderer on the React side.

Read the Vue guide pnpm add @jasy/vue@alpha vue
Invoice.vue
<Document>
  <Page :gap="12">
    <Text :size="30" bold>{{ invoice.title }}</Text>

    <Row
      v-for="line in invoice.lines"
      :key="line.id"
      :justify="'between'"
    >
      <Text>{{ line.name }}</Text>
      <Text bold>{{ line.total }} €</Text>
    </Row>
  </Page>
</Document>
const bytes = await renderToPdf(Invoice) // → a PDF, right in the browser
  • Components & props

    Author a document the way you build an app - reusable components, props and slots.

  • v-for, v-if, computed

    Your reactive data drives the page; loops, conditions and computed values just work.

  • Renders in the browser

    The isomorphic engine turns the component tree into a PDF client-side - no server required.

@jasy/nuxt · the Nuxt module

new · on npm

Drop it into Nuxt .

One install, zero config. Author PDFs as components and render them right in the browser, or stream them from a server route - your choice, the same components and helpers on both sides.

Read the Nuxt guide npx nuxi module add @jasy/nuxt
in the browser
<script setup>
const { open, download } = usePdf(Invoice)
</script>

<template>
  <button @click="open">View PDF</button>
</template>
on a server route
// server/api/invoice/[id].get.ts
export default definePdfHandler((e) =>
  Document([
    Page([
      Text(`Invoice #${getRouterParam(e, "id")}`),
    ]),
  ]),
)
  • Zero config

    Add it to modules. Components and the render helpers are auto-imported - no wiring, no imports.

  • Client or server

    Render in the browser with usePdf, or stream from a Nitro route with definePdfHandler. Same vocabulary.

  • Nitro caching, lean bundle

    Cache a route per path + query in one option. jimp stays server-side - the browser bundle stays small.

roadmap · MIT · built in the open

1.0 is built. Here's what's next.

Built in the open, MIT-licensed. Everything the 1.0 set out to do is done and shipping; this is what comes after it - in order, no dates promised.

  • Read and edit any PDF

    Open a file, change what is on the page, and merge, split or extract. Reading a form and filling it already ships.

  • Digital signatures

    Sign a document so a receiver can prove nobody touched it afterwards - the piece an e-invoice still misses.

  • The last few percent of layout

    Paint order inside a positioning frame, a public measure() helper, true multi-column, and a border that stays open where a box is split across pages.

  • Templates you can start from

    An invoice, a report and a letter that work as they are, and come apart when you want your own.

  • More e-invoice profiles

    Peppol and further country formats, on top of the Factur-X, ZUGFeRD and XRechnung profiles already supported.

  • Our own bidi

    Hebrew and Arabic already order correctly. The UAX #9 library behind that seam has not been touched since 2023 and ships no types, so it is going to be ours.

Follow on GitHub See the full board Issues, ideas and PRs welcome.
Image jasypdf

Declarative PDFs in pure TypeScript. ZUGFeRD & XRechnung compliant, with no headless browser and no Java.

Resources

© 2026 Florian Heuberger · MIT License

Built with Nuxt · self-hosted fonts · no trackers