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.

Tables are one of the most powerful layout tools in pdfmake. A table node wraps a table object that contains at minimum a body array — a two-dimensional array where each inner array is a row, and each element within that row is a cell. Cells can contain any content node pdfmake supports, including nested tables.

Basic table structure

const docDefinition = {
  content: [
    {
      table: {
        body: [
          ['Column 1', 'Column 2', 'Column 3'],
          ['One value goes here', 'Another one here', 'OK?']
        ]
      }
    }
  ]
};
Cells can hold rich content — stacks, lists, nested tables, or inline styled text:
{
  table: {
    body: [
      ['Column 1', 'Column 2', 'Column 3'],
      [
        {
          stack: [
            "Let's try an unordered list",
            { ul: ['item 1', 'item 2'] }
          ]
        },
        [
          'or a nested table',
          {
            table: {
              body: [
                ['Col1', 'Col2', 'Col3'],
                ['1', '2', '3'],
                ['1', '2', '3']
              ]
            }
          }
        ],
        {
          text: [
            'Inlines can be ',
            { text: 'styled\n', italics: true },
            { text: 'easily as everywhere else', fontSize: 10 }
          ]
        }
      ]
    ]
  }
}

Column widths

The widths property accepts an array with one entry per column. Omitting widths distributes space evenly.
{
  table: {
    widths: [100, '*', 200, '*'],
    body: [
      ['width=100', 'star-sized', 'width=200', 'star-sized'],
      ['fixed-width cells have exactly the specified width',
       { text: 'nothing interesting here', italics: true, color: 'gray' },
       { text: 'nothing interesting here', italics: true, color: 'gray' },
       { text: 'nothing interesting here', italics: true, color: 'gray' }]
    ]
  }
}
Width valueBehaviour
100 (number)Fixed width in points
'*'Star-sized — shares remaining space equally with other stars
'auto'Shrinks to fit content
'50%'Percentage of the available width

Row heights

heights controls the minimum height of each row. It accepts a number, an array, or a function:
{
  table: {
    heights: [20, 50, 70],
    body: [
      ['row 1 with height 20', 'column B'],
      ['row 2 with height 50', 'column B'],
      ['row 3 with height 70', 'column B']
    ]
  }
}
Use a function for dynamic heights:
{
  table: {
    heights: function (row) {
      return (row + 1) * 25;
    },
    body: [
      ['row 1', 'column B'],
      ['row 2', 'column B'],
      ['row 3', 'column B']
    ]
  }
}

Column and row spans

Set colSpan and rowSpan on a cell object. Placeholder empty strings '' must fill the skipped slots so every row has the correct number of cells.
{
  table: {
    widths: [200, 'auto', 'auto'],
    headerRows: 2,
    body: [
      [
        { text: 'Header with Colspan = 2', style: 'tableHeader', colSpan: 2, alignment: 'center' },
        {},
        { text: 'Header 3', style: 'tableHeader', alignment: 'center' }
      ],
      [
        { text: 'Header 1', style: 'tableHeader', alignment: 'center' },
        { text: 'Header 2', style: 'tableHeader', alignment: 'center' },
        { text: 'Header 3', style: 'tableHeader', alignment: 'center' }
      ],
      ['Sample value 1', 'Sample value 2', 'Sample value 3'],
      [
        { rowSpan: 3, text: 'rowSpan set to 3\nLorem ipsum ...' },
        'Sample value 2', 'Sample value 3'
      ],
      ['', 'Sample value 2', 'Sample value 3'],
      ['Sample value 1', 'Sample value 2', 'Sample value 3'],
      [
        'Sample value 1',
        { colSpan: 2, rowSpan: 2, text: 'Both rowSpan and colSpan\ncan be defined at the same time' },
        ''
      ],
      ['Sample value 1', '', '']
    ]
  }
}
When using colSpan, pad the skipped cells with empty objects {} rather than empty strings if the skipped cells need a specific border or fill color.

Header rows

headerRows marks the first N rows as headers. pdfmake automatically repeats them at the top of each new page when the table spans multiple pages.
{
  table: {
    headerRows: 1,
    body: [
      [
        { text: 'Header 1', style: 'tableHeader' },
        { text: 'Header 2', style: 'tableHeader' },
        { text: 'Header 3', style: 'tableHeader' }
      ],
      ['Sample value 1', 'Sample value 2', 'Sample value 3'],
      // ... more rows
    ]
  }
}
Use keepWithHeaderRows to prevent a page break between the header and the first N data rows:
{
  table: {
    headerRows: 1,
    keepWithHeaderRows: 1,   // keep at least one data row with the header
    body: [/* ... */]
  }
}
Use dontBreakRows: true to prevent any row from being split across pages:
{
  table: {
    dontBreakRows: true,
    body: [/* ... */]
  }
}

Built-in table layouts

pdfmake ships with three predefined layouts you can reference by name using the layout property on the wrapper node (not inside table):
{
  table: { headerRows: 1, body: [/* ... */] },
  layout: 'noBorders'
}

Custom table layouts

Pass a layout object directly to override every visual attribute. All functions receive the row index and the table node:
{
  table: {
    headerRows: 1,
    body: [/* ... */]
  },
  layout: {
    hLineWidth: function (i, node) {
      return (i === 0 || i === node.table.body.length) ? 2 : 1;
    },
    vLineWidth: function (i, node) {
      return (i === 0 || i === node.table.widths.length) ? 2 : 1;
    },
    hLineColor: function (i, node) {
      return (i === 0 || i === node.table.body.length) ? 'black' : 'gray';
    },
    vLineColor: function (i, node) {
      return (i === 0 || i === node.table.widths.length) ? 'black' : 'gray';
    }
    // paddingLeft: function(i, node) { return 4; },
    // paddingRight: function(i, node) { return 4; },
    // paddingTop: function(i, node) { return 2; },
    // paddingBottom: function(i, node) { return 2; },
    // fillColor: function (rowIndex, node, columnIndex) { return null; }
  }
}

Dashed borders

hLineStyle and vLineStyle return a dash descriptor:
layout: {
  hLineStyle: function (i, node) {
    if (i === 0 || i === node.table.body.length) { return null; }
    return { dash: { length: 10, space: 4 } };
  },
  vLineStyle: function (i, node) {
    if (i === 0 || i === node.table.widths.length) { return null; }
    return { dash: { length: 4 } };
  }
}

Zebra stripes

{
  table: { body: [/* ... */] },
  layout: {
    fillColor: function (rowIndex, node, columnIndex) {
      return (rowIndex % 2 === 0) ? '#CCCCCC' : null;
    }
  }
}

Fill color with opacity gradient

{
  table: { body: [/* 8 rows */] },
  layout: {
    fillColor: 'blue',
    fillOpacity: function (rowIndex, node, columnIndex) {
      return (rowIndex / 8 + columnIndex / 3);
    }
  }
}

Cell-level properties

Individual cells accept style properties in addition to layout-controlled ones:
[
  { text: 'Sample value 1', fillColor: 'blue', fillOpacity: 0.15 },
  { text: 'Sample value 2', fillColor: 'blue', fillOpacity: 0.60 },
  { text: 'Sample value 3', fillColor: 'blue', fillOpacity: 0.85 }
]

Per-cell borders

border accepts a four-element boolean array — [left, top, right, bottom]:
{
  table: {
    body: [
      [
        { border: [false, true, false, false], fillColor: '#eeeeee', text: 'top border only' },
        { border: [false, false, false, false], fillColor: '#dddddd', text: 'no borders' },
        { border: [true, true, true, true], fillColor: '#eeeeee', text: 'all borders' }
      ]
    ]
  },
  layout: { defaultBorder: false }
}
layout.defaultBorder controls whether cells without an explicit border property show borders (true by default). Setting it to false lets you opt-in per cell.

Per-cell border colors

borderColor accepts a four-element color array — [left, top, right, bottom]:
{
  text: 'Header with Colspan = 3',
  colSpan: 3,
  borderColor: ['#ff00ff', '#00ffff', '#ff00ff', '#00ffff'],
  alignment: 'center'
}

Overlay patterns on cells

Cells support an additional overlayPattern and overlayOpacity to composite a pattern on top of a solid fill:
{
  text: 'Sample value 1',
  fillColor: 'blue',
  fillOpacity: 0.15,
  overlayPattern: ['stripe45d', 'gray'],
  overlayOpacity: 0.15
}

Named table layouts

Register layouts by name using pdfmake.addTableLayouts() before calling createPdf(). Once registered, any table in any document can reference them by name using the layout property:
const docDefinition = { /* ... */ };

pdfmake.addTableLayouts({
  exampleLayout: {
    hLineWidth: function (i, node) { return 1; },
    vLineWidth: function (i, node) { return 0; },
    hLineColor: function (i, node) { return '#aaa'; },
    paddingLeft: function (i, node) { return 8; },
    paddingRight: function (i, node) { return 8; }
  }
});

pdfmake.createPdf(docDefinition);
Then reference the layout by name in any table wrapper node:
{
  table: { body: [/* ... */] },
  layout: 'exampleLayout'
}

Build docs developers (and LLMs) love