Skip to main content

Data Source

The source property populates tables and lists from arrays in your data object. This is how you generate dynamic-length content -- tables with a variable number of rows, or lists with a variable number of items.

Table data source

Properties

PropertyTypeDescription
sourcestring$data path pointing to an array of objects
mapstring[]Array of object keys, mapped to columns left-to-right

How it works

  1. Define the table with its widths and a header row in rows.
  2. Set source to a $data path pointing to an array of objects.
  3. Set map to specify which object keys map to which columns.
  4. At generation time, one row is appended for each item in the array, with values pulled from the keys listed in map.

The header row you define in rows is preserved. Data rows are appended after it.

Template

{
"table": {
"source": "$data.items",
"map": ["description", "qty", "amount"],
"widths": [3, 1, 1],
"rows": [
[
{ "p": "[b]Description[/b]" },
{ "p": "[b]Qty[/b]" },
{ "p": "[b]Amount[/b]" }
]
]
}
}

Data

{
"items": [
{ "description": "Cloud migration", "qty": "120", "amount": "$18,000" },
{ "description": "Security audit", "qty": "40", "amount": "$6,000" },
{ "description": "Training sessions", "qty": "16", "amount": "$2,400" }
]
}

Result

The table renders with the header row plus three data rows:

DescriptionQtyAmount
Cloud migration120$18,000
Security audit40$6,000
Training sessions16$2,400

Column mapping

The map array maps object keys to columns positionally:

  • map[0] maps to the first column
  • map[1] maps to the second column
  • map[2] maps to the third column

The number of entries in map should match the number of columns defined in widths.

List data source

A data-driven list uses source (and optionally map). For an array of strings, each string becomes a list item directly. For an array of objects, supply map with a $item.* template.

Properties

PropertyTypeDescription
sourcestring$data path pointing to an array
mapstringOptional template for each item (e.g. "$item.name — $item.role")

How it works

  1. Define the list (ul or ol) with source inside it.
  2. Set source to a $data path pointing to an array.
  3. At generation time, one list item is created for each entry in the array.

When source is supplied, omit li — the items are produced from the data.

Unordered list

{
"ul": {
"source": "$data.notes"
}
}

Ordered list

{
"ol": {
"source": "$data.paymentTerms"
}
}

Data

{
"notes": [
"Payment due within 30 days",
"Late fees apply after due date",
"Contact billing@acme.com for questions"
],
"paymentTerms": [
"Wire transfer to ShipForge Ltd account",
"Reference: INV-SF-2026-0329",
"Net 30 days from invoice date"
]
}

Combined example: table and list

This example uses both a table source for line items and a list source for payment terms in a single document.

Template

{
"document": {
"styles": {
"title": { "fontSize": 20, "bold": true },
"subtitle": { "fontSize": 12, "italic": true, "color": "#555555" },
"total": { "bold": true, "fontSize": 14 }
},
"content": [
{ "p": "$data.document.title", "style": "title" },
{ "p": "Prepared for $data.client.companyName on $data.document.date", "style": "subtitle" },
{
"separator": {
"lineType": "SOLID",
"length": 500,
"width": 1,
"color": "#000000"
}
},
{
"table": {
"source": "$data.lineItems",
"map": ["service", "hours", "rate", "total"],
"widths": [4, 1, 1, 1],
"rows": [
[
{ "p": "[b]Service[/b]" },
{ "p": "[b]Hours[/b]" },
{ "p": "[b]Rate[/b]" },
{ "p": "[b]Total[/b]" }
]
]
}
},
{ "p": "Grand Total: $data.invoice.grandTotal", "style": "total" },
{ "p": "Payment Terms:" },
{
"ol": {
"source": "$data.paymentTerms"
}
},
{ "p": "Thank you for your business, $data.client.contactName." }
]
}
}

Data

{
"document": {
"title": "Professional Services Invoice",
"date": "March 29, 2026"
},
"client": {
"companyName": "Meridian Logistics",
"contactName": "Sarah Mitchell"
},
"lineItems": [
{ "service": "Platform setup", "hours": "80", "rate": "$150", "total": "$12,000" },
{ "service": "API integration", "hours": "40", "rate": "$175", "total": "$7,000" },
{ "service": "User training", "hours": "16", "rate": "$125", "total": "$2,000" }
],
"invoice": {
"grandTotal": "$21,000"
},
"paymentTerms": [
"Wire transfer to ShipForge Ltd account",
"Reference: INV-SF-2026-0329",
"Net 30 days from invoice date"
]
}

Result

The generated PDF contains:

  1. A heading "Professional Services Invoice" with the subtitle "Prepared for Meridian Logistics on March 29, 2026"
  2. A table with a header row and three data rows for the line items
  3. The grand total "Grand Total: $21,000"
  4. A numbered list of payment terms
  5. A closing line "Thank you for your business, Sarah Mitchell."

Key points

  • source and map are placed as sibling properties alongside the table or ul/ol object, not inside it.
  • The header row in a table is preserved; data rows are appended after it.
  • For lists, the li array should be empty when using source -- items are generated from the data array.
  • source values are $data paths, just like scalar references.
  • You can combine scalar $data references and source in the same document.