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
| Property | Type | Default | Description |
|---|---|---|---|
title | string | "Table" | Heading displayed above the table. Supports {FIELDNAME} tokens for live data values. |
titleIcon | select | none | Decorative icon next to the title. Accepts any key from the icon registry (e.g. package, list, calendar). |
accentColor | color | theme.primary | Color applied to the title text and icon. |
showTitle | toggle | true | When disabled, the title row is completely omitted - useful when the section heading already names the table. |
Data Binding
| Property | Type | Default | Description |
|---|---|---|---|
dataPath | string | "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.
| Property | Type | Default | Description |
|---|---|---|---|
key | string | none | Data field name - must match a key on each row object. |
label | string | none | Column header text. |
width | number | "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. |
format | string | "text" | Formatting hint applied at render time. Supported values: text, number, currency, date, datetime, boolean, percentage. |
subText | string | none | Static 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. borderStyleis set to"minimal".
Border & Line Styles
| Property | Type | Default | Description |
|---|---|---|---|
showBorders | boolean | true | Master 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. |
borderColor | color | theme.borderColor | Color applied to all table lines. |
vLineWidth | number | 0.5 | Width (pt) of all vertical divider lines. Only applied when borderStyle is "full". |
hLineWidth | number | 0.5 | Width (pt) of horizontal row-separator lines. |
headerLineWidth | number | 1.5 | Width (pt) of the thick line that separates the header row from the first data row. |
Header & Stripe Styling
| Property | Type | Default | Description |
|---|---|---|---|
headerBackground | color | theme.primary | Fill color of the header row. |
headerText | color | theme.textOnPrimary | Text color for header labels. |
stripeShade | color | theme.stripeShade | Fill color applied to every other data row (zebra stripes). |
stripeOpacity | number (0-1) | 1 | Opacity 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.
| Property | Path | Description |
|---|---|---|
grouping.by | props.grouping.by | Field name used as the group key. Rows with the same value are placed in the same band. |
grouping.header.show | props.grouping.header.show | Show a full-width header row at the top of each group band. Default: true. |
grouping.header.template | props.grouping.header.template | Template string for the group header cell. Supports {{fieldName}} tokens. Default: the raw group-key value. |
grouping.footer.show | props.grouping.footer.show | Show a full-width footer row at the bottom of each group band. Default: false. |
grouping.footer.template | props.grouping.footer.template | Template string for the group footer cell. |
grouping.pageBreakPerGroup | props.grouping.pageBreakPerGroup | Insert a page break before every group band except the first. |
grouping.keepGroupTogether | props.grouping.keepGroupTogether | Keep 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.
| Property | Description |
|---|---|
op | Aggregate function: sum, avg, count, min, max. |
label | Text displayed in the first cell of the aggregate row (e.g. "Total", "Subtotal"). |
targetColumnId / targetColumn / key | Identifies 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
| Property | Path | Default | Description |
|---|---|---|---|
breakBefore | pagination.breakBefore | false | Insert a page break immediately before the table block. |
breakAfter | pagination.breakAfter | false | Insert a page break immediately after the table block. |
keepTogether | pagination.keepTogether | false | Prevent the document engine from splitting the table across pages. |
tableBreakAfterNRows | pagination.tableBreakAfterNRows | none | Force a page break after every N data rows. Useful for very tall tables that must start fresh every page. |
Conditional Visibility
| Property | Path | Description |
|---|---|---|
visibleWhen | expressions.visibleWhen | JavaScript expression evaluated against the JSON payload. When the expression returns falsy, the entire table block is omitted from the document. |
valueWhen | expressions.valueWhen | Expression that replaces the rendered value at runtime (advanced use). |
Examples
Theme-driven table (recommended)
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"
}
}Spacer / Separator
A lightweight visual divider and vertical spacing control. Renders as a horizontal rule (solid or dashed) with configurable thickness, color, and surrounding whitespace, or as an invisible spacer block when the line is hidden.
Hierarchical Table
A multi-level adaptive table in VaultPDF. Renders deeply nested JSON arrays as progressive depth tables with per-level styling, overflow sub-rows, conditional cell styles, computations, and hierarchyRules-driven layout.