Template Components

Table

The standard data table in VaultPDF. Renders a sorted or grouped list of records from the JSON payload with configurable columns, aggregates, borders, and stripe styling.

Overview

A Table (table) binds to an array in the JSON payload (via dataPath) and renders every record as a table row. Each column definition maps a data key to a header label with optional formatting, alignment, and a secondary sub-text line. Tables support grouping by a field, per-group and overall aggregates, overflow sub-rows with per-cell conditional styles, and a full set of border and stripe overrides.


Title & Icon

PropertyTypeDefaultDescription
titlestring"Table"Heading displayed above the table. Supports {FIELDNAME} tokens for live data values.
titleIconselectnoneDecorative icon next to the title. Accepts any key from the icon registry (e.g. package, list, calendar).
accentColorcolortheme.primaryColor applied to the title text and icon.
showTitletoggletrueWhen disabled, the title row is completely omitted - useful when the section heading already names the table.

Data Binding

PropertyTypeDefaultDescription
dataPathstring"items"Dot-path into the JSON payload that resolves to the array of records. Example: "lines", "order.lineItems".

Columns

Each entry in columns describes one vertical slice of the table.

PropertyTypeDefaultDescription
keystringnoneData field name - must match a key on each row object.
labelstringnoneColumn header text.
widthnumber | "auto" | "*""*"Column width in pt, "auto" (fit content), or "*" (fill remaining space).
align"left" | "right" | "center""left"Text alignment for both the header and data cells.
formatstring"text"Formatting hint applied at render time. Supported values: text, number, currency, date, datetime, boolean, percentage.
subTextstringnoneStatic secondary line rendered below the main cell value in a smaller gray font (8pt). Useful for units or descriptions.

Mixing Column Widths

Column widths follow the same rules as the underlying document engine. Mix fixed pt values with "*" fill columns - for example [80, "*", 60] - to achieve proportional layouts.


Column Manifest (Advanced)

When a columnManifest is provided, the table switches to advanced column mode, which unlocks overflow sub-rows and per-cell conditional styles. The plain columns array is ignored when a columnManifest is present.

Advanced column mode is also engaged automatically when:

  • Any column has primary: false (overflow column).
  • Any column defines conditionalStyles.
  • borderStyle is set to "minimal".

Border & Line Styles

PropertyTypeDefaultDescription
showBordersbooleantrueMaster switch. When false, all lines are suppressed.
borderStyle"full" | "minimal""full""full" - full grid with outer frame, vertical dividers, and row separators. "minimal" - row separators only; outer frame and vertical lines are hidden. Automatically engages the manifest renderer.
borderColorcolortheme.borderColorColor applied to all table lines.
vLineWidthnumber0.5Width (pt) of all vertical divider lines. Only applied when borderStyle is "full".
hLineWidthnumber0.5Width (pt) of horizontal row-separator lines.
headerLineWidthnumber1.5Width (pt) of the thick line that separates the header row from the first data row.

Header & Stripe Styling

PropertyTypeDefaultDescription
headerBackgroundcolortheme.primaryFill color of the header row.
headerTextcolortheme.textOnPrimaryText color for header labels.
stripeShadecolortheme.stripeShadeFill color applied to every other data row (zebra stripes).
stripeOpacitynumber (0-1)1Opacity of the zebra-stripe fill. Set to 0 to disable stripes entirely.

Theme Tokens vs Hex Values

The color properties above support theme token strings (e.g. "theme.primary", "theme.borderColor") as well as explicit hex values. Omitting a property entirely falls back to the active theme automatically - only set these when you want to override the theme for a specific table.


Grouping

Grouping splits table rows into visually separated bands keyed by a field value.

PropertyPathDescription
grouping.byprops.grouping.byField name used as the group key. Rows with the same value are placed in the same band.
grouping.header.showprops.grouping.header.showShow a full-width header row at the top of each group band. Default: true.
grouping.header.templateprops.grouping.header.templateTemplate string for the group header cell. Supports {{fieldName}} tokens. Default: the raw group-key value.
grouping.footer.showprops.grouping.footer.showShow a full-width footer row at the bottom of each group band. Default: false.
grouping.footer.templateprops.grouping.footer.templateTemplate string for the group footer cell.
grouping.pageBreakPerGroupprops.grouping.pageBreakPerGroupInsert a page break before every group band except the first.
grouping.keepGroupTogetherprops.grouping.keepGroupTogetherKeep each group band on a single page, preventing splits across page boundaries.

Group Key Column

The column whose key matches grouping.by is automatically removed from the table columns so it does not appear as a redundant data column.


Aggregates

Aggregate rows appear as summary lines below the table data (or per group band). Each aggregate rule defines an operation, a label, and the target column where the result is rendered.

PropertyDescription
opAggregate function: sum, avg, count, min, max.
labelText displayed in the first cell of the aggregate row (e.g. "Total", "Subtotal").
targetColumnId / targetColumn / keyIdentifies the column that receives the computed value. Checked against column id first, then key.

Configure table-level aggregates via props.aggregates[]. Configure per-group aggregates via props.grouping.aggregates[].


Pagination

PropertyPathDefaultDescription
breakBeforepagination.breakBeforefalseInsert a page break immediately before the table block.
breakAfterpagination.breakAfterfalseInsert a page break immediately after the table block.
keepTogetherpagination.keepTogetherfalsePrevent the document engine from splitting the table across pages.
tableBreakAfterNRowspagination.tableBreakAfterNRowsnoneForce a page break after every N data rows. Useful for very tall tables that must start fresh every page.

Conditional Visibility

PropertyPathDescription
visibleWhenexpressions.visibleWhenJavaScript expression evaluated against the JSON payload. When the expression returns falsy, the entire table block is omitted from the document.
valueWhenexpressions.valueWhenExpression that replaces the rendered value at runtime (advanced use).

Examples

Omit color properties to inherit the active theme. Use "theme.*" tokens to reference theme values explicitly without hardcoding hex.

{
  "type": "table",
  "props": {
    "title": "Order Lines",
    "titleIcon": "list",
    "dataPath": "lines",
    "showTitle": true,
    "columns": [
      { "key": "partNumber",  "label": "Part #",      "width": 80,  "align": "left" },
      { "key": "description", "label": "Description", "width": "*",  "align": "left" },
      { "key": "qty",         "label": "Qty",         "width": 50,  "align": "right", "format": "number"   },
      { "key": "unitPrice",   "label": "Unit Price",  "width": 70,  "align": "right", "format": "currency" },
      { "key": "lineTotal",   "label": "Total",       "width": 70,  "align": "right", "format": "currency" }
    ],
    "aggregates": [
      { "op": "sum", "label": "Grand Total", "key": "lineTotal" }
    ],
    "stripeOpacity": 0.5,
    "borderStyle": "full"
  }
}

With explicit color overrides

Use "theme.*" tokens to pin to theme values, or provide a hex string to fully override.

{
  "type": "table",
  "props": {
    "title": "Order Lines",
    "titleIcon": "list",
    "dataPath": "lines",
    "columns": [
      { "key": "partNumber",  "label": "Part #",      "width": 80,  "align": "left" },
      { "key": "description", "label": "Description", "width": "*",  "align": "left" },
      { "key": "qty",         "label": "Qty",         "width": 50,  "align": "right", "format": "number"   },
      { "key": "unitPrice",   "label": "Unit Price",  "width": 70,  "align": "right", "format": "currency" },
      { "key": "lineTotal",   "label": "Total",       "width": 70,  "align": "right", "format": "currency" }
    ],
    "aggregates": [
      { "op": "sum", "label": "Grand Total", "key": "lineTotal" }
    ],
    "headerBackground": "theme.primary",
    "headerText": "theme.textOnPrimary",
    "borderColor": "theme.borderColor",
    "stripeShade": "theme.stripeShade",
    "stripeOpacity": 0.5,
    "borderStyle": "full"
  }
}

Grouped table with aggregates

{
  "type": "table",
  "props": {
    "title": "Lines by Warehouse",
    "dataPath": "lines",
    "columns": [
      { "key": "partNumber",  "label": "Part #",      "width": 80,  "align": "left" },
      { "key": "description", "label": "Description", "width": "*",  "align": "left" },
      { "key": "qty",         "label": "Qty",         "width": 50,  "align": "right", "format": "number" },
      { "key": "warehouse",   "label": "Warehouse",   "width": 80,  "align": "left" }
    ],
    "grouping": {
      "by": "warehouse",
      "header": { "show": true, "template": "Warehouse: {{warehouse}}" },
      "footer": { "show": false },
      "pageBreakPerGroup": false,
      "keepGroupTogether": true,
      "aggregates": [
        { "op": "sum", "label": "Subtotal Qty", "key": "qty" }
      ]
    },
    "aggregates": [
      { "op": "sum", "label": "Grand Total Qty", "key": "qty" }
    ],
    "borderStyle": "full"
  }
}

Minimal border style with conditional status column

{
  "type": "table",
  "props": {
    "title": "Shipments",
    "dataPath": "shipments",
    "borderStyle": "minimal",
    "columnManifest": {
      "maxCols": 4,
      "columns": [
        { "key": "trackingNumber", "label": "Tracking #",   "displayOrder": 1, "primary": true, "width": 100, "align": "left" },
        { "key": "carrier",        "label": "Carrier",       "displayOrder": 2, "primary": true, "width": 80,  "align": "left" },
        { "key": "eta",            "label": "ETA",           "displayOrder": 3, "primary": true, "width": 80,  "align": "left", "format": "date" },
        { "key": "status",         "label": "Status",        "displayOrder": 4, "primary": true, "width": 80,  "align": "center",
          "conditionalStyles": [
            { "value": "Delivered",  "icon": "CheckCircle",   "color": "#10B981", "bgColor": "#ECFDF5" },
            { "value": "In Transit", "icon": "AlertTriangle", "color": "#B45309", "bgColor": "#FEF3C7" },
            { "value": "Exception",  "icon": "XCircle",       "color": "#EF4444", "bgColor": "#FEF2F2" },
            { "value": "default",    "icon": "",              "color": "#374151", "bgColor": "#F9FAFB" }
          ]
        }
      ],
      "overflowStrategy": {
        "mode": "additionalRow",
        "columnsPerRow": 2,
        "indent": 8,
        "columnGap": 6,
        "borderStyle": "dashed",
        "fontSizeAdjustment": -1
      }
    }
  }
}

Overflow columns: grid layout

Columns with primary: false render as key-value pairs in a grid sub-row beneath each data row. columnsPerRow controls how many pairs appear side-by-side.

{
  "type": "table",
  "props": {
    "title": "Purchase Orders",
    "dataPath": "orders",
    "columnManifest": {
      "maxCols": 5,
      "columns": [
        { "key": "poNumber",   "label": "PO #",       "displayOrder": 1, "primary": true,  "width": 80,  "align": "left" },
        { "key": "vendor",     "label": "Vendor",     "displayOrder": 2, "primary": true,  "width": "*", "align": "left" },
        { "key": "orderDate",  "label": "Order Date", "displayOrder": 3, "primary": true,  "width": 80,  "align": "left",  "format": "date" },
        { "key": "total",      "label": "Total",      "displayOrder": 4, "primary": true,  "width": 80,  "align": "right", "format": "currency" },
        { "key": "shipVia",    "label": "Ship Via",   "displayOrder": 5, "primary": false, "align": "left" },
        { "key": "trackingNo", "label": "Tracking #", "displayOrder": 6, "primary": false, "align": "left" },
        { "key": "warehouse",  "label": "Warehouse",  "displayOrder": 7, "primary": false, "align": "left" },
        { "key": "payTerms",   "label": "Pay Terms",  "displayOrder": 8, "primary": false, "align": "left" }
      ],
      "overflowStrategy": {
        "mode": "additionalRow",
        "columnsPerRow": 2,
        "indent": 12,
        "columnGap": 8,
        "borderStyle": "dashed",
        "fontSizeAdjustment": -1
      }
    }
  }
}

Overflow columns: full-span rows

Use span: "full" for long-text fields that should occupy the entire row width. Full-span rows always get their own line and can carry an icon and custom fill.

{
  "type": "table",
  "props": {
    "title": "Contracts",
    "dataPath": "contracts",
    "columnManifest": {
      "maxCols": 4,
      "columns": [
        { "key": "contractId",   "label": "Contract #",   "displayOrder": 1, "primary": true,  "width": 90,  "align": "left" },
        { "key": "counterparty", "label": "Counterparty", "displayOrder": 2, "primary": true,  "width": "*", "align": "left" },
        { "key": "startDate",    "label": "Start",        "displayOrder": 3, "primary": true,  "width": 80,  "align": "left", "format": "date" },
        { "key": "endDate",      "label": "End",          "displayOrder": 4, "primary": true,  "width": 80,  "align": "left", "format": "date" },
        {
          "key": "summary",    "label": "Summary",    "displayOrder": 5, "primary": false,
          "layout": { "span": "full", "icon": "FileText", "showTitle": true }
        },
        {
          "key": "legalNotes", "label": "Legal Notes", "displayOrder": 6, "primary": false,
          "layout": {
            "span": "full", "icon": "Shield", "showTitle": true,
            "style": { "bgColor": "#FEF3C7", "textColor": "#92400E", "fontWeight": "normal" }
          }
        }
      ],
      "overflowStrategy": {
        "mode": "additionalRow",
        "columnsPerRow": 2,
        "indent": 0,
        "columnGap": 8,
        "borderStyle": "solid",
        "fontSizeAdjustment": 0
      }
    }
  }
}

Mixed overflow: grid pairs and full-span rows

Grid pairs (span: "auto") and full-span rows (span: "full") can coexist in the same manifest. Ordering is by displayOrder - grid pairs accumulate until a full-span column is encountered, which flushes the partial grid row first.

"columns": [
  { "key": "id",       "label": "ID",       "displayOrder": 1, "primary": true,  "width": 60 },
  { "key": "name",     "label": "Name",     "displayOrder": 2, "primary": true,  "width": "*" },
  { "key": "amount",   "label": "Amount",   "displayOrder": 3, "primary": true,  "width": 80, "format": "currency" },
  { "key": "category", "label": "Category", "displayOrder": 4, "primary": false },
  { "key": "region",   "label": "Region",   "displayOrder": 5, "primary": false },
  {
    "key": "notes", "label": "Notes", "displayOrder": 6, "primary": false,
    "layout": { "span": "full", "icon": "FileText" }
  },
  {
    "key": "tags", "label": "Tags", "displayOrder": 7, "primary": false,
    "layout": { "span": "full", "displayAsList": true, "showTitle": false }
  }
]

showTitle: false on Full-span Columns

showTitle: false on a full-span column omits the label cell entirely - the value spans the full row width. Useful for plain narrative text that needs no heading.

Overflow with separator between grid pairs

"overflowStrategy": {
  "mode": "additionalRow",
  "columnsPerRow": 3,
  "indent": 12,
  "columnGap": 10,
  "borderStyle": "dashed",
  "fontSizeAdjustment": -1,
  "separator": {
    "enabled": true,
    "type": "|",
    "width": 8,
    "color": "#CBD5E1"
  }
}

On this page