Skip to content

PDF export

Problem: users want a “Download PDF” button next to the CSV and Excel ones — a paginated, print-ready table that matches what they see on screen.

The Export plugin ships CSV, Excel XML and JSON because all three are string formats it can emit with no dependency. PDF is a binary format that needs a layout engine and embedded fonts: the smallest credible writer is larger than the entire grid. So the plugin instead exposes the same data accessors it uses internally, and a PDF export is a dozen lines against public API.

Either library works; pick on size and how much layout control you need.

LibraryApprox. size (min+gz)Notes
pdfmake~350 kB incl. default fontsDeclarative document definition; handles page breaks and repeated header rows for you
jsPDF + AutoTable~150 kBSmaller; per-column and per-cell style hooks

Both are large enough that you should load them lazily (await import(...)) from the button handler rather than at module scope.

Use mode: 'formatted' so the PDF shows the same strings the user sees on screen, and let getResolvedColumns() decide column order — that way onlyVisible, onlySelected, columns and rowIndices keep working for free.

import { queryGrid } from '@toolbox-web/grid';
export async function exportPdf(fileName = 'employees.pdf') {
const exporter = queryGrid('tbw-grid').getPluginByName('export');
if (!exporter) return;
const { default: pdfMake } = await import('pdfmake/build/pdfmake');
const { default: pdfFonts } = await import('pdfmake/build/vfs_fonts');
pdfMake.vfs = pdfFonts.vfs;
const columns = exporter.getResolvedColumns();
const rows = exporter.export({ mode: 'formatted' });
pdfMake
.createPdf({
pageOrientation: 'landscape',
content: [
{
table: {
headerRows: 1,
widths: columns.map(() => '*'),
body: [
columns.map((c) => ({ text: c.header ?? c.field, bold: true })),
...rows.map((row) => columns.map((c) => String(row[c.field] ?? ''))),
],
},
layout: 'lightHorizontalLines',
},
],
})
.download(fileName);
}

Wire it in like any other export — from a toolbar button, or with exporter.export({ onlySelected: true, mode: 'formatted' }) for a “print selection” action.

  • getResolvedColumns() returns leaf columns. If the GroupingColumnsPlugin is installed and you want the group band above the leaf headers in the PDF too, build that extra header row from your own group config — processHeaderRow only affects the plugin’s own Excel and JSON output.
  • Wide grids overflow. Neither library reflows a table that’s wider than the page. Cap the column count with export({ columns: [...] }), or shrink the font, before blaming the writer.
  • Fonts are not free. pdfmake’s default VFS embeds Roboto; non-Latin scripts need a custom font bundle, which is usually larger than the writer itself.
  • mode: 'formatted' runs your column.format functions. If one of them returns a DOM node rather than a string you’ll get [object HTMLElement] in the PDF — that’s what renderer is for, and renderers never participate in export.
AI assistants: For complete API documentation, implementation guides, and code examples for this library, see https://toolboxjs.com/llms-full.txt