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.
Overview
A Hierarchical Table (hierarchical_table) is VaultPDF's most powerful data rendering node. It recursively processes any nested array structure (e.g. orders, line items, serial numbers) and renders each depth level as its own table, indented beneath the parent row.
Place one hierarchical_table node in your template and bind it to a top-level array in the payload. Visual defaults adapt automatically to the data volume, and hierarchyRules give you fine-grained control over every depth level.
Title & Icon
| Property | Type | Default | Description |
|---|---|---|---|
title | string | none | Heading rendered above the table. Supports {FIELDNAME} tokens. |
titleIcon | select | none | Icon key displayed next to the title (e.g. package, user, calendar). |
id | string | none | Unique identifier for this node. Required when multiple hierarchical tables exist in the same template to prevent column display conflicts between nodes. |
Data Binding
| Property | Path | Description |
|---|---|---|
dataSource | props.dataSource | Placeholder that resolves to the root array at render time. Must use double-brace syntax: {{arrayFieldName}}. Example: {{salesOrders}}. |
maxCols | props.maxCols | Maximum number of columns that the adaptive renderer will display per depth level before auto-truncating. |
margin | props.margin | Four-element pt margin [left, top, right, bottom] applied to the entire rendered block. |
hierarchyRules
hierarchyRules is the JSON object that controls everything about how each depth level of the hierarchy is displayed. It lives at the top level of your JSON payload (or on the template's docDefinition) and is threaded through to the adaptive renderer automatically.
{
"hierarchyRules": {
"fieldVisibility": { },
"levelStyling": { "2": { }, "3": { } },
"computations": [ ]
}
}fieldVisibility
Controls which fields are shown or hidden per depth level. Values can be:
true/false- always show / always hide across all levels.- A JavaScript expression string (evaluated with
leveland the row's data context in scope).
"fieldVisibility": {
"internalCode": false,
"adjustedQty": "level >= 3 && adjustedQty > 0"
}levelStyling
levelStyling is a map from depth-level number (as a string key, e.g. "2", "3") to a LevelStyling object that overrides the defaults for that level.
Level numbering: Level 1 is the root scalar/header section. Level 2 is the first array (main table). Level 3 is a nested array within each level-2 row, and so on.
Default behavior by level:
| Level | Default borderStyle | Default fontSize | Default format |
|---|---|---|---|
| 2 | "full" | 10pt | "table" |
| 3 | "minimal" | 9pt | "table" |
| 4 | "none" | 8.5pt | "table" |
| 5+ | "none" | 8pt | "compact-kv" |
Theme-Aware Colors
Color properties in levelStyling are theme-aware by default when omitted. The renderer falls back to theme.primary for headerBackground, theme.textOnPrimary for headerText, theme.borderColor for borderColor, and theme.stripeShade for stripeShade. Only set these explicitly when you need to override the active theme for a specific level.
LevelStyling Properties
| Property | Type | Description |
|---|---|---|
fontSize | number | Font size (pt) for all data cells at this level. |
borderStyle | "full" | "minimal" | "none" | "full" - full grid. "minimal" - row separators only. "none" - no borders. |
borderColor | string | Hex color for all table lines at this level. |
color | string | Default text color for data cells. |
fillColor | string | Row background color applied to all rows (bypasses zebra stripes when set). |
indentation | number | Left indent (pt) added to the nested table to visually represent hierarchy depth. |
maxColumns | number | Maximum columns rendered at this level (extra columns are truncated). Default: 20. |
showHeaders | boolean | Show or hide the header row at this level. Default: true for levels 2-4. |
format | "table" | "compact-kv" | "mini-table" | Rendering format. "table" - standard row/column grid. "compact-kv" - key-value pairs in a tight two-column layout. "mini-table" - compact table with reduced padding. |
hiddenColumns | number[] | Zero-based column indices to suppress from the output at this level. |
showSummary | boolean | Render a summary bar below this level's table. |
summaryFields | string[] | Field names included in the summary bar (requires showSummary: true). |
headerBackground | string | Hex color override for the header row fill color at this level. |
headerText | string | Hex color override for header label text at this level. |
stripeShade | string | Hex color override for zebra-stripe rows at this level. |
stripeOpacity | number (0-1) | Opacity of zebra stripes. 0 disables stripes entirely, 1 is full opacity. |
vLineWidth | number | Vertical line width (pt) - only applied when borderStyle is "full". |
hLineWidth | number | Horizontal row-separator line width (pt). |
headerLineWidth | number | Header separator line width (pt). |
titleIcon | string | Icon key shown next to the section/table title at this level in the adaptive renderer. |
columnFormats | Record<string, string> | Lightweight per-field format map - alternative to a full columnManifest. Keys are field names; values are format strings ("currency", "number", "date", "datetime", "boolean"). |
columnManifest | ColumnManifest | Full column manifest (see Column Manifest below). When present, overrides all auto-detected column layout for this level. |
Theme-driven (recommended):
Omitting color properties entirely lets every table inherit the active theme automatically. Only set what you want to override.
"hierarchyRules": {
"levelStyling": {
"2": {
"fontSize": 10,
"borderStyle": "full",
"stripeOpacity": 0.4,
"columnFormats": { "unitPrice": "currency", "qty": "number", "shipDate": "date" }
},
"3": {
"fontSize": 9,
"borderStyle": "minimal",
"format": "compact-kv"
}
}
}With explicit color overrides:
Provide hex values only when the active theme should be overridden for a specific level.
"hierarchyRules": {
"levelStyling": {
"2": {
"fontSize": 10,
"borderStyle": "full",
"headerBackground": "#1E3A5F",
"headerText": "#FFFFFF",
"borderColor": "#CBD5E1",
"stripeShade": "#F1F5F9",
"stripeOpacity": 0.5
}
}
}Theme Token Mapping
The Universal Adaptive Renderer maps theme tokens to visual elements as follows. All three render paths use the same mapping.
Header Background Colors
| Level | Token Resolved | Fallback Chain |
|---|---|---|
| Level 2 (main table) | primary | → #F0F0F0 |
| Level 3+ (nested tables) | secondary | → headerShade → #DBEAFE |
"colors": {
"primary": "#1D4ED8", // Level 2 header fill (bold branded bar)
"secondary": "#E0F2FE", // Level 3+ header fill (lighter nested header)
"headerShade": "#F4F4F6" // Fallback for level 3+ when secondary is absent
}Token Precedence
secondary and headerShade are distinct tokens and never cross-resolve. If your theme defines headerShade but not secondary, level 3+ headers fall back through headerShade. If both are defined, secondary always wins for nested table headers.
Full Theme Token Reference
| Token | Used For | Example |
|---|---|---|
primary | Level 2 table header fill; card accent bars; primary buttons | "#1D4ED8" |
secondary | Level 3+ table header fill; accent fills | "#E0F2FE" |
headerShade | Fallback header fill when secondary is absent | "#F4F4F6" |
stripeShade | Zebra-stripe row background | "#F9FAFB" |
textOnPrimary | Header text on primary-colored cells | "#FFFFFF" |
textOnShade | Header text on secondary/headerShade-colored cells; body text | "#0F172A" |
borderColor | All table lines, card borders, separator lines | "#BFDBFE" |
secondaryText | Accented value text (e.g., links, highlighted values) | "#0673BC" |
success | Conditional-style icon/text for success states | "#16A34A" |
warning | Conditional-style icon/text for warning states | "#D97706" |
error | Conditional-style icon/text for error states | "#DC2626" |
info | Conditional-style icon/text for informational states | "#2563EB" |
Theme Source Priority
The renderer resolves the active theme in the following order (first match wins):
- Payload
themeblock — a"theme"key at the root of the API request body. - Data
themeblock — a"theme"key inside the data payload JSON itself. - Template
themeblock — the"theme"object in the.vpdftemplate file.
In production, theme is almost always supplied by the template. The payload theme is only used for one-off overrides during testing. If no theme is found, all colors fall back to built-in defaults.
// Template theme block (recommended — lives in the .vpdf file)
"theme": {
"type": "modern",
"colors": {
"primary": "#1D4ED8",
"secondary": "#E0F2FE",
"headerShade": "#F4F4F6",
"stripeShade": "#F9FAFB",
"textOnPrimary": "#FFFFFF",
"textOnShade": "#0F172A",
"borderColor": "#BFDBFE"
},
"typography": { "font": "Inter" }
}computations
Computations are aggregation rules applied before rendering. They allow derived fields (sums, counts, averages) to be computed from deeper arrays and written back onto the parent-level records at render time.
| Property | Description |
|---|---|
level | The depth level where the computation result is written (e.g. 2 = write onto the main array row). |
field | The field name to write the result into on the parent record. |
label | Optional display label used in summary bars (e.g. "Total Quantity"). |
type | Aggregate function: "sum", "count", "avg", "min", "max", or "formula". |
formula | Math expression string (e.g. "quantity * unitPrice") evaluated per record. Required when type is "formula". |
source | Nested array field name to aggregate from (e.g. "bins" to sum bins[].quantity). |
aggregateField | Field name within the source array to aggregate (e.g. "quantity"). |
condition | JavaScript expression that must be truthy for a record to be included (e.g. "quantity > 0"). |
format | Optional format hint applied to the result when rendering ("currency", "number", etc.). |
Example - sum quantities from a nested bins array onto each order row:
"computations": [
{
"level": 2,
"field": "totalQty",
"label": "Total Quantity",
"type": "sum",
"source": "bins",
"aggregateField": "quantity",
"format": "number"
},
{
"level": 2,
"field": "lineTotal",
"type": "formula",
"formula": "qty * unitPrice",
"format": "currency"
}
]Column Manifest
A ColumnManifest gives you full, explicit control over column layout, overflow rows, and per-cell conditional styling. It can be placed either directly in the template node's props.columnManifest or inside hierarchyRules.levelStyling["2"].columnManifest.
Minimal starting point:
"columnManifest": {
"maxCols": 5,
"columns": [
{ "key": "id", "label": "ID", "displayOrder": 1, "primary": true, "width": 60, "align": "left" },
{ "key": "description", "label": "Description", "displayOrder": 2, "primary": true, "width": "*", "align": "left" },
{ "key": "qty", "label": "Qty", "displayOrder": 3, "primary": true, "width": 50, "align": "right", "format": "number" },
{ "key": "unitPrice", "label": "Unit Price", "displayOrder": 4, "primary": true, "width": 70, "align": "right", "format": "currency" },
{ "key": "notes", "label": "Notes", "displayOrder": 5, "primary": false,
"layout": { "span": "full", "icon": "FileText" }
}
],
"overflowStrategy": {
"mode": "additionalRow",
"columnsPerRow": 2,
"indent": 12,
"columnGap": 8,
"borderStyle": "dashed",
"fontSizeAdjustment": -1
}
}ColumnManifest Properties
| Property | Type | Description |
|---|---|---|
maxCols | number | Informational hint - the actual visible column count is driven by primary flags on each ColumnDefinition. |
columns | ColumnDefinition[] | Ordered list of all columns (primary and overflow). |
overflowStrategy | OverflowStrategy | Describes how non-primary columns are rendered below each data row. |
allCaps | boolean | When true, all data-cell text is rendered in upper case. Header labels are unaffected. Default: false. |
fontSize | number | Override the font size (pt) for every data cell in the table. |
valueBold | boolean | When true, all data-cell values are rendered bold. Default: false. |
ColumnDefinition Properties
| Property | Type | Description |
|---|---|---|
key | string | Data field name - maps to the row object's property. |
label | string | Column header text. |
displayOrder | number | 1-based sort order. Lower values appear first. Must be unique per manifest. |
primary | boolean | true - rendered in the main row. false - rendered in the overflow sub-row. |
width | number | "auto" | "*" | Column width passed to the document engine. |
align | "left" | "right" | "center" | Text alignment for header and data cells. |
format | string | Format hint: text, number, currency, date, datetime, boolean, percentage, barcode, qr. |
labelWidth | number | Fixed pt width for the label portion inside an overflow key-value pair. |
currency | Partial<CurrencySettings> | Per-column currency override (wins over templateSettings.currency). |
number | Partial<NumberSettings> | Per-column number format override. |
boolean | Partial<BooleanSettings> | Per-column boolean label/icon override. |
fontSize | number | Per-column font size (overrides manifest-level fontSize). |
allCaps | boolean | Per-column upper-case override. |
valueBold | boolean | Per-column bold override. |
conditionalStyles | ConditionalStyle[] | Value-driven per-cell styling rules (see below). |
layout | object | Overflow layout options (see below). |
Conditional Styles
conditionalStyles is an ordered list of ConditionalStyle rules. The first matching rule wins. Use "default" as the value for a catch-all fallback.
"conditionalStyles": [
{ "value": "Success", "icon": "CheckCircle", "color": "#10B981", "bgColor": "#ECFDF5" },
{ "value": "Fail", "icon": "XCircle", "color": "#EF4444", "bgColor": "#FEF2F2" },
{ "value": "Warning", "icon": "AlertTriangle", "color": "#B45309", "bgColor": "#FEF3C7" },
{ "value": "default", "icon": "", "color": "#374151", "bgColor": "#F9FAFB" }
]| Property | Description |
|---|---|
value | Cell value to match (case-insensitive). Use "default" for a catch-all. |
icon | Icon key from the icon registry, or "" for no icon. |
color | Text color (and icon stroke) when the rule matches. Hex string. |
bgColor | Cell background fill color when the rule matches. Hex string. |
Column Layout (Overflow)
The optional layout object on a ColumnDefinition controls how that column appears in the overflow sub-row.
| Property | Type | Description |
|---|---|---|
span | "auto" | "full" | "auto" (default) - participates in the grid. "full" - spans the full overflow row width; any partially-filled grid row is flushed first. |
icon | string | Icon key rendered before the label. Only meaningful when span: "full". |
displayAsList | boolean | When true and the column value is an array, render each element as a bullet-point line. Only meaningful when span: "full". |
showTitle | boolean | When false, the label cell is omitted and the value spans the full row width. Default: true. Only meaningful when span: "full". |
style.bgColor | string | Override overflow row background fill. |
style.textColor | string | Override overflow row text color. |
style.fontWeight | "bold" | "normal" | Override overflow row font weight. |
OverflowStrategy Properties
| Property | Type | Description |
|---|---|---|
mode | "additionalRow" | Currently the only supported mode. Non-primary columns appear as an additional sub-row per data row. |
columnsPerRow | number | Number of overflow key-value pairs rendered side-by-side per sub-row line. |
indent | number | Left margin (pt) of the overflow cell - creates visual indentation. |
columnGap | number | Gap (pt) between neighboring key-value pairs within one sub-row line. |
borderStyle | "dashed" | "solid" | "none" | Style of the separator line between the main row and the overflow sub-row. "dashed" is recommended. |
fontSizeAdjustment | number | Signed delta applied to the base font size for overflow content (e.g. -1 to make overflow text slightly smaller). |
separator.enabled | boolean | Show a vertical separator glyph between grid pairs. |
separator.type | string | Character(s) to use as the separator (e.g. "|", "•"). |
separator.width | number | Column width (pt) reserved for the separator glyph. Default: 6. |
separator.color | string | Hex color for the separator. Falls back to theme.borderColor. |
Sections (Level-1 Key-Value)
When the JSON payload root is a plain object (not an array), the Universal Adaptive Renderer automatically renders its scalar fields as a header key-value section before the first array table. This section is referred to as the Level-1 section.
- Scalar fields and primitive arrays are collected and rendered as bold
key: valuepairs. - Plain nested objects (non-array) are rendered as indented sub-sections with their own bold headers.
- Record arrays at the root level become the Level-2 tables.
Fields are excluded from the Level-1 section when:
- Their key starts with
_. - They are reserved control keys:
theme,activeTheme,metadata,hierarchyRules,__meta,__schema. fieldVisibilitysets them tofalse.
Tip
To control which scalars appear in the header section, use hierarchyRules.fieldVisibility to hide fields you don't need at the top level. For example: "fieldVisibility": { "internalId": false, "rawPayload": false }.
Auto-Generated Sections
Each record array within the JSON payload produces a titled section block in the rendered document. VaultPDF assigns a title to each block derived from the array's key name (formatted to title case). These sections:
- Can be numbered sequentially when
templateSettings.sectionNumberingistrue. - Can appear in the Table of Contents when
templateSettings.toc.enabledistrue. - Respect per-section TOC exclusions via
hierarchyRulesor section settings.
Styling (Inspector)
The following properties are exposed in the Designer inspector under the Styling panel for hierarchical_table nodes:
| Property | Path | Description |
|---|---|---|
stripeOpacity | props.stripeOpacity | Opacity of zebra-stripe rows across all levels (0-1). Overrides the levelStyling value for the root table. |
Deeper per-level overrides (header colors, borders, line widths) are set via hierarchyRules.levelStyling in the JSON payload.
Pagination
Pagination for hierarchical tables is controlled by the parent section's or node's pagination settings. Block margins are set automatically based on the volume of data in the payload.
Examples
1 - Simple flat list (no hierarchy)
The simplest case: a single-level array, theme-driven colors, basic column formats.
{
"hierarchyRules": {
"levelStyling": {
"2": {
"fontSize": 10,
"borderStyle": "full",
"stripeOpacity": 0.4,
"columnFormats": {
"unitPrice": "currency",
"qty": "number",
"shipDate": "date"
}
}
}
},
"items": [
{ "partNumber": "P-1001", "description": "Widget Alpha", "qty": 5, "unitPrice": 49.99, "shipDate": "2026-03-20" }
]
}2 - Overflow columns: grid layout
Overflow columns with primary: false and span: "auto" are placed in a key-value grid below each main row. columnsPerRow controls how many pairs sit side-by-side.
{
"hierarchyRules": {
"levelStyling": {
"2": {
"fontSize": 10,
"borderStyle": "full",
"columnManifest": {
"maxCols": 5,
"columns": [
{ "key": "partNumber", "label": "Part #", "displayOrder": 1, "primary": true, "width": 80, "align": "left" },
{ "key": "description", "label": "Description", "displayOrder": 2, "primary": true, "width": "*", "align": "left" },
{ "key": "qty", "label": "Qty", "displayOrder": 3, "primary": true, "width": 50, "align": "right", "format": "number" },
{ "key": "unitPrice", "label": "Unit Price", "displayOrder": 4, "primary": true, "width": 70, "align": "right", "format": "currency" },
{ "key": "warehouse", "label": "Warehouse", "displayOrder": 5, "primary": false, "align": "left" },
{ "key": "batchNumber", "label": "Batch #", "displayOrder": 6, "primary": false, "align": "left" },
{ "key": "expiryDate", "label": "Expiry", "displayOrder": 7, "primary": false, "align": "left", "format": "date" },
{ "key": "supplier", "label": "Supplier", "displayOrder": 8, "primary": false, "align": "left" }
],
"overflowStrategy": {
"mode": "additionalRow",
"columnsPerRow": 2,
"indent": 12,
"columnGap": 8,
"borderStyle": "dashed",
"fontSizeAdjustment": -1
}
}
}
}
},
"items": [
{
"partNumber": "P-1001", "description": "Widget Alpha", "qty": 5, "unitPrice": 49.99,
"warehouse": "WH-A", "batchNumber": "B-20260301", "expiryDate": "2027-03-01", "supplier": "Acme Corp"
}
]
}Empty Overflow Columns
Overflow columns are skipped automatically when their value is empty or null for a given row - no blank sub-rows appear.
3 - Overflow columns: full-span rows
Use span: "full" for long text fields (notes, descriptions, addresses) that should occupy the entire row width. Full-span rows always appear on their own line and flush any in-progress grid row first.
{
"hierarchyRules": {
"levelStyling": {
"2": {
"fontSize": 10,
"borderStyle": "full",
"columnManifest": {
"maxCols": 4,
"columns": [
{ "key": "id", "label": "ID", "displayOrder": 1, "primary": true, "width": 60, "align": "left" },
{ "key": "subject", "label": "Subject", "displayOrder": 2, "primary": true, "width": "*", "align": "left" },
{ "key": "priority", "label": "Priority", "displayOrder": 3, "primary": true, "width": 70, "align": "center" },
{ "key": "status", "label": "Status", "displayOrder": 4, "primary": true, "width": 70, "align": "center" },
{
"key": "description", "label": "Description", "displayOrder": 5, "primary": false,
"layout": { "span": "full", "icon": "FileText", "showTitle": true }
},
{
"key": "resolution", "label": "Resolution", "displayOrder": 6, "primary": false,
"layout": {
"span": "full", "icon": "CheckCircle", "showTitle": true,
"style": { "bgColor": "#ECFDF5", "textColor": "#065F46", "fontWeight": "bold" }
}
}
],
"overflowStrategy": {
"mode": "additionalRow",
"columnsPerRow": 2,
"indent": 0,
"columnGap": 8,
"borderStyle": "solid",
"fontSizeAdjustment": 0
}
}
}
}
},
"tickets": [
{
"id": "T-001", "subject": "Login failure on mobile", "priority": "High", "status": "Resolved",
"description": "Users on iOS 17 cannot log in after the March update. Affects all accounts using SSO.",
"resolution": "Rolled back SSO provider to v2.3.1. Permanent fix scheduled for April sprint."
}
]
}4 - Mixed overflow: grid pairs and full-span rows
Combine span: "auto" grid columns with span: "full" rows in the same manifest. Grid pairs render first (in column order), then full-span rows follow - each flushing any partial grid row before starting.
"columnManifest": {
"maxCols": 5,
"columns": [
{ "key": "partNumber", "label": "Part #", "displayOrder": 1, "primary": true, "width": 80 },
{ "key": "description", "label": "Description", "displayOrder": 2, "primary": true, "width": "*" },
{ "key": "qty", "label": "Qty", "displayOrder": 3, "primary": true, "width": 50, "format": "number" },
{ "key": "unitPrice", "label": "Unit Price", "displayOrder": 4, "primary": true, "width": 70, "format": "currency" },
{ "key": "warehouse", "label": "Warehouse", "displayOrder": 5, "primary": false },
{ "key": "batchNumber", "label": "Batch #", "displayOrder": 6, "primary": false },
{
"key": "notes", "label": "Notes", "displayOrder": 7, "primary": false,
"layout": { "span": "full", "icon": "FileText" }
},
{
"key": "tags", "label": "Tags", "displayOrder": 8, "primary": false,
"layout": { "span": "full", "icon": "Tag", "displayAsList": true }
}
],
"overflowStrategy": {
"mode": "additionalRow",
"columnsPerRow": 2,
"indent": 12,
"columnGap": 8,
"borderStyle": "dashed",
"fontSizeAdjustment": -1
}
}displayAsList
displayAsList: true renders an array value as a bullet-point list, one element per line. Only meaningful on span: "full" columns.
5 - Overflow with vertical separator
Add a visual | separator between key-value pairs in the grid row.
"overflowStrategy": {
"mode": "additionalRow",
"columnsPerRow": 3,
"indent": 12,
"columnGap": 10,
"borderStyle": "dashed",
"fontSizeAdjustment": -1,
"separator": {
"enabled": true,
"type": "|",
"width": 8,
"color": "#CBD5E1"
}
}6 - showSummary with computations
showSummary renders a themed summary bar (primary color fill, right-aligned) at the bottom of each level-2 item block. Fields listed in summaryFields are excluded from the main table columns and shown only in the bar.
The typical pattern:
- Add a computation that aggregates nested data into a field on the parent row.
- List that field name in
summaryFields. - Enable
showSummary: true.
{
"hierarchyRules": {
"levelStyling": {
"2": {
"fontSize": 10,
"borderStyle": "full",
"showSummary": true,
"summaryFields": ["totalQty", "totalValue"]
},
"3": {
"fontSize": 9,
"borderStyle": "minimal"
}
},
"computations": [
{
"level": 2,
"field": "totalQty",
"label": "Total Qty",
"type": "sum",
"source": "lines",
"aggregateField": "qty",
"format": "number"
},
{
"level": 2,
"field": "totalValue",
"label": "Order Value",
"type": "sum",
"source": "lines",
"aggregateField": "lineTotal",
"format": "currency"
}
]
},
"orders": [
{
"orderNumber": "SO-1001",
"customer": "Acme Corp",
"lines": [
{ "partNumber": "P-101", "description": "Widget", "qty": 10, "lineTotal": 499.90 },
{ "partNumber": "P-102", "description": "Gadget", "qty": 5, "lineTotal": 249.95 }
]
}
]
}Summary Bar Format
The summary bar displays as: Total Qty: 15 | Order Value: $749.85 - using a pipe delimiter between fields. Field labels come from the label property on the matching computation, falling back to a formatted version of the field name.
7 - Three-level hierarchy
Level 2 = orders (full grid), level 3 = line items (minimal borders), level 4 = serial numbers (compact key-value). Each level inherits theme colors unless overridden.
{
"hierarchyRules": {
"fieldVisibility": {
"_internal": false
},
"levelStyling": {
"2": {
"fontSize": 10,
"borderStyle": "full",
"stripeOpacity": 0.3,
"showSummary": true,
"summaryFields": ["totalLines"]
},
"3": {
"fontSize": 9,
"borderStyle": "minimal",
"indentation": 8,
"columnFormats": { "unitPrice": "currency", "qty": "number" }
},
"4": {
"fontSize": 8,
"borderStyle": "none",
"format": "compact-kv",
"indentation": 16,
"showHeaders": false
}
},
"computations": [
{
"level": 2,
"field": "totalLines",
"label": "Line Count",
"type": "count",
"source": "lines"
}
]
},
"orders": [
{
"orderNumber": "SO-1001",
"customer": "Acme Corp",
"orderDate": "2026-03-15",
"lines": [
{
"partNumber": "P-101", "qty": 10, "unitPrice": 49.99,
"serials": [
{ "serialNumber": "SN-001", "condition": "New" },
{ "serialNumber": "SN-002", "condition": "New" }
]
}
]
}
]
}8 - Conditional cell styles on a status column
{
"key": "status",
"label": "Status",
"displayOrder": 5,
"primary": true,
"width": 80,
"align": "center",
"conditionalStyles": [
{ "value": "Active", "icon": "CheckCircle", "color": "#10B981", "bgColor": "#ECFDF5" },
{ "value": "Pending", "icon": "AlertTriangle", "color": "#B45309", "bgColor": "#FEF3C7" },
{ "value": "Cancelled", "icon": "XCircle", "color": "#EF4444", "bgColor": "#FEF2F2" },
{ "value": "On Hold", "icon": "AlertTriangle", "color": "#7C3AED", "bgColor": "#EDE9FE" },
{ "value": "default", "icon": "", "color": "#374151", "bgColor": "#F9FAFB" }
]
}conditionalStyles also work on overflow columns in the sub-row - the icon and colors are applied identically regardless of whether the column is primary: true or primary: false.
Full Example
A complete, ready-to-paste payload. Color properties on levelStyling are omitted so they inherit the active theme automatically - add explicit hex overrides only when you need to deviate from the theme.
{
"hierarchyRules": {
"fieldVisibility": {
"internalRef": false
},
"levelStyling": {
"2": {
"fontSize": 10,
"borderStyle": "full",
"stripeOpacity": 0.4,
"columnManifest": {
"maxCols": 6,
"columns": [
{ "key": "partNumber", "label": "Part #", "displayOrder": 1, "primary": true, "width": 80, "align": "left" },
{ "key": "description", "label": "Description", "displayOrder": 2, "primary": true, "width": "*", "align": "left" },
{ "key": "qty", "label": "Qty", "displayOrder": 3, "primary": true, "width": 50, "align": "right", "format": "number" },
{ "key": "unitPrice", "label": "Unit Price", "displayOrder": 4, "primary": true, "width": 70, "align": "right", "format": "currency" },
{ "key": "status", "label": "Status", "displayOrder": 5, "primary": true, "width": 70, "align": "center",
"conditionalStyles": [
{ "value": "Active", "icon": "CheckCircle", "color": "#10B981", "bgColor": "#ECFDF5" },
{ "value": "On Hold", "icon": "AlertTriangle", "color": "#B45309", "bgColor": "#FEF3C7" },
{ "value": "Cancelled", "icon": "XCircle", "color": "#EF4444", "bgColor": "#FEF2F2" },
{ "value": "default", "icon": "", "color": "#374151", "bgColor": "#F9FAFB" }
]
},
{ "key": "notes", "label": "Notes", "displayOrder": 6, "primary": false,
"layout": { "span": "full", "icon": "FileText", "showTitle": true }
}
],
"overflowStrategy": {
"mode": "additionalRow",
"columnsPerRow": 2,
"indent": 12,
"columnGap": 8,
"borderStyle": "dashed",
"fontSizeAdjustment": -1
}
}
},
"3": {
"fontSize": 9,
"borderStyle": "minimal",
"format": "table"
}
},
"computations": [
{
"level": 2,
"field": "lineTotal",
"type": "formula",
"formula": "qty * unitPrice",
"format": "currency"
}
]
},
"salesOrders": [
{
"partNumber": "P-1001",
"description": "Widget Alpha",
"qty": 5,
"unitPrice": 49.99,
"status": "Active",
"notes": "Expedite required - customer priority order.",
"bins": [
{ "binCode": "A-12", "quantity": 3 },
{ "binCode": "B-07", "quantity": 2 }
]
}
]
}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.
Conditional Styles & Advanced Formatting
Guide to the conditional styling system and advanced formatting options available for cards, tables, hierarchical tables, and note blocks.