Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/bpampuch/pdfmake/llms.txt

Use this file to discover all available pages before exploring further.

By default, every node in content stacks vertically one below the other. pdfmake’s columns node breaks that flow and places child nodes side-by-side in a single row. Columns follow the same width model as table columns — fixed numbers, 'auto', or '*' (star) — and can contain any content type including other columns, lists, tables, and images.

Basic columns

const docDefinition = {
  content: [
    'By default paragraphs are stacked one on top of another.',
    'Here we go with 2 star-sized columns, with justified text and gap set to 20:\n\n',
    {
      alignment: 'justify',
      columns: [
        {
          text: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Malit profecta versatur nomine ...'
        },
        {
          text: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Malit profecta versatur nomine ...'
        }
      ]
    }
  ]
};

Column width values

Width valueBehaviour
*Star-sized — all star columns share remaining space equally
'auto'Shrinks to fit its own content
90 (number)Fixed width in points
'50%'Percentage of available width

Star columns

Star-sized columns always have equal widths:
{
  columns: [
    { text: 'Column 1 ...' },
    { text: 'Column 2 ...' },
    { text: 'Column 3 ...' }
  ]
}

Mixed widths

Fix some columns and let stars fill the rest:
{
  columns: [
    { width: 90,  text: 'Fixed 90pt column ...' },
    { width: '*', text: 'Star-sized column ...' },
    { width: '*', text: 'Star-sized column ...' },
    { width: 90,  text: 'Fixed 90pt column ...' }
  ]
}

Auto columns

Auto columns size to their content:
{
  columns: [
    { width: 'auto', text: 'auto column' },
    { width: '*',    text: 'This is a star-sized column ...' },
    { width: 50,     text: 'this one has specific width set to 50' },
    { width: 'auto', text: 'another auto column' },
    { width: '*',    text: 'This is a star-sized column ...' }
  ]
}
If all columns are auto-sized and their combined width is less than the page width, the columns do not expand to fill the available space — they remain as wide as their content requires.

columnGap

columnGap sets the horizontal space between columns. It can be specified per columns node or in defaultStyle to apply globally:
const docDefinition = {
  content: [
    {
      columns: [ /* ... */ ],
      columnGap: 20
    }
  ],
  defaultStyle: {
    columnGap: 20   // applies to every columns node
  }
};

Nested columns

Each column is itself a full content context — you can nest columns nodes to any depth:
{
  columns: [
    {
      width: 100,
      fontSize: 9,
      text: 'Lorem ipsum ...'
    },
    [
      'This column is defined as an array, so it stacks paragraphs vertically.',
      "Let's divide the remaining space into 3 star-sized columns:\n\n",
      {
        columns: [
          { text: 'Lorem ipsum ...' },
          { text: 'Lorem ipsum ...' },
          { text: 'Lorem ipsum ...' }
        ]
      }
    ]
  ]
}
A column defined as an array [...] rather than an object is treated as a vertical stack of paragraphs inside that column — equivalent to { stack: [...] }.

The stack node

Use { stack: [...] } to group multiple nodes vertically inside a single column without using an array shorthand:
{
  columns: [
    {
      width: '50%',
      stack: [
        { text: 'Features', bold: true },
        { text: '• High performance' },
        { text: '• Low power consumption' }
      ]
    },
    {
      width: '50%',
      stack: [
        { text: 'Specifications', bold: true },
        { text: '• Weight: 2.5 kg' },
        { text: '• Dimensions: 10x20x5 cm' }
      ]
    }
  ],
  columnGap: 10
}

Style inheritance

Columns inherit styles from a parent style property. Individual columns can override any inherited property:
{
  style: 'bigger',   // fontSize: 15, italics: true
  columns: [
    'First column inherits bigger style',
    {
      fontSize: 20,
      text: "fontSize overridden to 20 but italics still inherited"
    },
    {
      style: 'header',   // fontSize: 18, bold: true
      text: 'Last column applies header style on top of bigger'
    }
  ]
}

Snaking columns (newspaper-style)

Set snakingColumns: true on a columns node to make content flow from the bottom of one column into the top of the next, exactly like a newspaper layout:
const loremIpsum = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. ...';

const docDefinition = {
  content: [
    {
      columns: [
        { text: loremIpsum.repeat(10), width: '*' },
        { text: '', width: '*' }     // empty target column
      ],
      columnGap: 30,
      snakingColumns: true
    }
  ]
};
Content placed after a snaking columns block continues normally on the same page:
{
  columns: [
    { text: loremIpsum.repeat(10), width: '*' },
    { text: '', width: '*' }
  ],
  columnGap: 30,
  snakingColumns: true
},
{ text: 'This text correctly appears after the snaking columns section.' }
Snaking columns with mixed widths also work. Place all content in the first column and leave the remaining columns empty — pdfmake overflows content automatically.
// Narrow left column + wide right column
{
  columns: [
    { text: loremIpsum.repeat(15), width: 200, fontSize: 10 },
    { text: '', width: '*' }
  ],
  columnGap: 30,
  snakingColumns: true
}

Five-column snaking across pages

{
  columns: [
    { text: loremIpsum.repeat(25), fontSize: 8, width: '*' },
    { text: '', width: '*' },
    { text: '', width: '*' },
    { text: '', width: '*' },
    { text: '', width: '*' }
  ],
  columnGap: 20,
  snakingColumns: true
}

absolutePosition for overlay positioning

absolutePosition removes a node from the normal flow and places it at fixed page coordinates. Any content type — text, images, tables, canvas — supports it:
const docDefinition = {
  content: [
    { image: 'bee', width: 50, height: 50, absolutePosition: { x: 100, y: 100 } },
    { image: 'bee', width: 50, height: 50, absolutePosition: { x: 150, y: 150 } },
    { text: 'You can put images at any position', pageBreak: 'after' },

    // Text at absolute positions
    { text: 'As',   absolutePosition: { x: 100, y: 100 } },
    { text: 'well', absolutePosition: { x: 150, y: 150 } },
    { text: 'as',   absolutePosition: { x: 200, y: 200 } },
    { text: 'text', absolutePosition: { x: 250, y: 150 } },
    { text: '!!!',  absolutePosition: { x: 300, y: 100 } }
  ],
  images: { bee: 'data:image/png;base64,...' }
};

relativePosition for offset positioning

relativePosition shifts a node by { x, y } from where it would normally appear in flow, without removing it from the layout:
{
  table: {
    widths: [100, 100, 100],
    body: [
      [
        'Column with a lot of text ...',
        {
          text: "I'm aligned center",
          style: { alignment: 'center' },
          relativePosition: { x: 0, y: 25 }
        },
        {
          text: "I'm aligned right",
          style: { alignment: 'right' },
          relativePosition: { x: 0, y: 25 }
        }
      ]
    ]
  }
}
relativePosition is particularly useful inside tables where the absolute page position of a cell is not known at document-definition time.

Columns vs. snaking columns

FeaturecolumnssnakingColumns: true
Content distributionEach column is independentContent flows from one column to the next
Use caseSide-by-side layouts, label+value pairsLong text, newspaper-style
Empty placeholder columnsNot neededRequired for target columns
Cross-page behaviourEach column can span pages independentlyOverflow continues into the next column on the same page

Build docs developers (and LLMs) love