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 value | Behaviour |
|---|
* | 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
| Feature | columns | snakingColumns: true |
|---|
| Content distribution | Each column is independent | Content flows from one column to the next |
| Use case | Side-by-side layouts, label+value pairs | Long text, newspaper-style |
| Empty placeholder columns | Not needed | Required for target columns |
| Cross-page behaviour | Each column can span pages independently | Overflow continues into the next column on the same page |