Documentation

Components

Every component maps to one engine element, and its props are typed - <Text :size :color bold> autocompletes and type-checks, and a wrong value (say a fit that does not exist) goes red in your editor. Boolean props have a shorthand: <Text bold> is <Text :bold="true">.

The full set:

Document · Page · Column · Row · Box · Padding · Expanded · Spacer · Divider · Image · Text · Paragraph · Span · Table / TableRow / TableCell · Positioned · DefaultTextStyle.

Structure

Document

The root of every PDF component. Its text props (font, size, color, lineHeight, align, bold, italic) set the document-wide defaults that everything inside inherits.

PropType
size, color, font, bold, italicdefault text style
align, lineHeightdefault paragraph style
meta{ title?, author? }
fontscustom fonts, see Data & assets

Page

One physical page. Lays its children out as a column, so gap, justify and align work here too.

PropType
size"A4", "Letter", ... or a custom size
orientation"portrait" | "landscape"
margina number, or per-side insets
gapspace between children
justify"start" | "center" | "end" | "between" | "around"
align"start" | "center" | "end" | "stretch"

<Page> also takes #header and #footer slots. They are laid out once and repeat on every physical page the content flows onto:

<template>
  <Document>
    <Page :size="'A4'" :margin="48">
      <template #header>
        <Text :size="9" color="#94a3b8">ACME GmbH - Invoice</Text>
      </template>

      <Text>... long content that paginates across pages ...</Text>

      <template #footer>
        <Row :justify="'between'">
          <Text :size="9" color="#94a3b8">acme.example</Text>
          <Text :size="9" color="#94a3b8">Page footer</Text>
        </Row>
      </template>
    </Page>
  </Document>
</template>

Layout

Column and Row

Stack children vertically (Column) or horizontally (Row). Both share gap, justify and align.

<template>
  <Row :gap="12" :justify="'between'" :align="'center'">
    <Text bold>Item</Text>
    <Text>Value</Text>
  </Row>
</template>

Box

A sized, decorated container: background, border, padding, rounded corners.

PropType
bg, bordera color
borderTop / borderRight / borderBottom / borderLeft, borderWidthper-side border control
paddinga number, or per-side insets
width, height, radiusnumbers (points)
relativemake it a positioning frame
overflow"hidden" clips children to the box

Padding, Spacer, Expanded, Divider

  • <Padding :insets="16"> - wraps a child in space (a number or per-side insets).
  • <Spacer :flex="1"> - flexible empty space that pushes siblings apart.
  • <Expanded :flex="1"> - a flex child that fills the remaining main-axis space.
  • <Divider color="#e2e8f0" :thickness="1" /> - a rule line.

Text

Text, Paragraph and Span

Text and Paragraph render text; Span styles a run inside them.

PropType
size, font, colorthe basics
bold, italicboolean shorthands
align"left" | "center" | "right"
lineHeightline spacing multiplier
maxLines, overflowoverflow is "clip" | "ellipsis"
<template>
  <Paragraph>
    Plain text, then a <Span bold color="#1450aa">styled run</Span> in the middle.
  </Paragraph>
</template>

bold and italic are left unset by default, so a <Text> inside a bold <DefaultTextStyle> stays bold unless you override it. <DefaultTextStyle> re-defaults the text style for a whole subtree, the per-section counterpart to the <Document> defaults.

Image

<template>
  <Image :src="bytes" :width="120" :height="120" :fit="'contain'" :radius="8" />
</template>

fit is "none", "contain", "cover" or "fill". src takes image bytes (a Uint8Array); see Data & assets for loading them in the browser.

Table

<Table :columns> holds <TableRow>s of <TableCell>s. Mark a row header to repeat it on every page the table paginates onto. Columns are widths: a number is fixed points, a string like "1fr" is a flexible share.

<template>
  <Table :columns="['1fr', 120]" cell-border="#e2e8f0" :cell-padding="9">
    <TableRow header>
      <TableCell><Text bold>Description</Text></TableCell>
      <TableCell><Text bold>Amount</Text></TableCell>
    </TableRow>
    <TableRow v-for="line in lines" :key="line.id">
      <TableCell
        ><Text>{{ line.name }}</Text></TableCell
      >
      <TableCell
        ><Text>{{ line.total }} EUR</Text></TableCell
      >
    </TableRow>
  </Table>
</template>

Table also takes gap / rowGap / colGap, cellPadding, cellBorder and a rule color.

Positioned

Out-of-flow placement, anchored to the nearest <Box relative> (or the page). Give it edges (top / right / bottom / left) or an alignment (h / v) plus an x / y offset.

<template>
  <Box relative :height="200">
    <Positioned :top="8" :right="8">
      <Text :size="9" color="#94a3b8">draft</Text>
    </Positioned>
  </Box>
</template>
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