Design

XLSX templates

Design a report in Excel, add Carbone tags, and generate it from JSON data as an Excel file or a PDF.
COMMUNITY FEATURE Available for:
Carbone Cloud
Carbone On-premise
Embedded Carbone JS
  v2.0+ 

Any XLSX file can be a template. Write Carbone tags in the cells, send your JSON data to the API, and get back an Excel file or a PDF with the layout of the template.

Basic

Write a tag in a cell to insert a value from the JSON data. In {d.company}, d is the root of the data. A cell can mix text and tags, as in row 8.

data.jsonDownload
{
  "title": "Q3 2026 sales report",
  "company": "Acme Corporation",
  "manager": "Jane Smith",
  "region": "North America",
  "period": "July to September 2026",
  "date": "2026-10-05"
}
template.xlsxDownload
XLSX template with Carbone tags in Excel cells
Cells of the first sheet of template.xlsx
RowAB
1{d.title}
3Company{d.company}
4Sales manager{d.manager}
5Region{d.region}
6Period{d.period}
8Report generated on {d.date:formatD('MMMM D, YYYY')} by {d.manager}.
report.xlsxDownload
Excel report generated by Carbone from JSON data
Cells of the first sheet of report.xlsx
RowAB
1Q3 2026 sales report
3CompanyAcme Corporation
4Sales managerJane Smith
5RegionNorth America
6PeriodJuly to September 2026
8Report generated on October 5, 2026 by Jane Smith.

Loops

A loop repeats a row for each item in an array. The template needs two rows:

Carbone copies the row with its styles and height, and moves the rows below it down, like the total in row 6.

data.jsonDownload
{
  "title": "Q3 2026 sales",
  "total": 21140,
  "products": [
    { "name": "Laptop",          "category": "Hardware", "qty": 12, "price": 950, "amount": 11400, "status": "In stock" },
    { "name": "27-inch monitor", "category": "Hardware", "qty": 25, "price": 240, "amount": 6000,  "status": "Out of stock" },
    { "name": "Keyboard",        "category": "Hardware", "qty": 60, "price": 35,  "amount": 2100,  "status": "In stock" },
    { "name": "Mouse",           "category": "Hardware", "qty": 80, "price": 12,  "amount": 960,   "status": "Backorder" },
    { "name": "Office license",  "category": "Software", "qty": 4,  "price": 99,  "amount": 396,   "status": "In stock" },
    { "name": "Antivirus",       "category": "Software", "qty": 3,  "price": 45,  "amount": 135,   "status": "Out of stock" },
    { "name": "Cloud backup",    "category": "Software", "qty": 1,  "price": 60,  "amount": 60,    "status": "In stock" },
    { "name": "Installation",    "category": "Services", "qty": 1,  "price": 50,  "amount": 50,    "status": "Backorder" },
    { "name": "Training",        "category": "Services", "qty": 1,  "price": 30,  "amount": 30,    "status": "In stock" },
    { "name": "Maintenance",     "category": "Services", "qty": 1,  "price": 9,   "amount": 9,     "status": "Out of stock" }
  ]
}
template.xlsxDownload
XLSX template with a loop on one row of a product table
Cells of the first sheet of template.xlsx
RowABCDEF
1{d.title}
3PRODUCTCATEGORYQUANTITYPRICEAMOUNTSTATUS
4{d.products[i].name}{d.products[i].category}{d.products[i].qty:formatN}{d.products[i].price:formatN}{d.products[i].amount:formatN}{d.products[i].status}
5{d.products[i+1].name}
6Total{d.total:formatN}
report.xlsxDownload
Excel report with one row per product generated by Carbone
Cells of the first sheet of report.xlsx
RowABCDEF
1Q3 2026 sales
3PRODUCTCATEGORYQUANTITYPRICEAMOUNTSTATUS
4LaptopHardware1295011400In stock
527-inch monitorHardware252406000Out of stock
6KeyboardHardware60352100In stock
7MouseHardware8012960Backorder
8Office licenseSoftware499396In stock
9AntivirusSoftware345135Out of stock
10Cloud backupSoftware16060In stock
11InstallationServices15050Backorder
12TrainingServices13030In stock
13MaintenanceServices199Out of stock
14Total21140

Frozen panes stay in place, so freeze only the rows above the loop.

Sort and filter

Add an attribute before i to sort the rows: {d.products[price, i].name} sorts the products by ascending price, and the end tag becomes [price+1, i+1]. Add a condition to the [i+1] tag to filter them: category!='Services' removes the services. Learn more about sorting and filtering.

data.jsonDownload
{
  "title": "Q3 2026 sales",
  "total": 21140,
  "products": [
    { "name": "Laptop",          "category": "Hardware", "qty": 12, "price": 950, "amount": 11400, "status": "In stock" },
    { "name": "27-inch monitor", "category": "Hardware", "qty": 25, "price": 240, "amount": 6000,  "status": "Out of stock" },
    { "name": "Keyboard",        "category": "Hardware", "qty": 60, "price": 35,  "amount": 2100,  "status": "In stock" },
    { "name": "Mouse",           "category": "Hardware", "qty": 80, "price": 12,  "amount": 960,   "status": "Backorder" },
    { "name": "Office license",  "category": "Software", "qty": 4,  "price": 99,  "amount": 396,   "status": "In stock" },
    { "name": "Antivirus",       "category": "Software", "qty": 3,  "price": 45,  "amount": 135,   "status": "Out of stock" },
    { "name": "Cloud backup",    "category": "Software", "qty": 1,  "price": 60,  "amount": 60,    "status": "In stock" },
    { "name": "Installation",    "category": "Services", "qty": 1,  "price": 50,  "amount": 50,    "status": "Backorder" },
    { "name": "Training",        "category": "Services", "qty": 1,  "price": 30,  "amount": 30,    "status": "In stock" },
    { "name": "Maintenance",     "category": "Services", "qty": 1,  "price": 9,   "amount": 9,     "status": "Out of stock" }
  ]
}
template.xlsxDownload
XLSX template with a loop sorted by price and filtered by category
Cells of the first sheet of template.xlsx
RowABC
1Hardware and software by price
3PRODUCTCATEGORYPRICE
4{d.products[price, i].name}{d.products[price, i].category}{d.products[price, i].price:formatN}
5{d.products[price+1, i+1, category!='Services'].name}
report.xlsxDownload
Excel rows sorted by price without the services
Cells of the first sheet of report.xlsx
RowABC
1Hardware and software by price
3PRODUCTCATEGORYPRICE
4MouseHardware12
5KeyboardHardware35
6AntivirusSoftware45
7Cloud backupSoftware60
8Office licenseSoftware99
927-inch monitorHardware240
10LaptopHardware950

Nested loops

A loop can contain another loop to group rows. {d.categories[i].name} repeats the categories, and {d.categories[i].products[i].name} repeats the products of each category. The [i+1] row of the categories closes the outer loop.

data.jsonDownload
{
  "title": "Q3 2026 sales by category",
  "categories": [
    {
      "name": "Hardware",
      "products": [
        {
          "name": "Laptop",
          "qty": 12,
          "amount": 11400
        },
        {
          "name": "27-inch monitor",
          "qty": 25,
          "amount": 6000
        },
        {
          "name": "Keyboard",
          "qty": 60,
          "amount": 2100
        },
        {
          "name": "Mouse",
          "qty": 80,
          "amount": 960
        }
      ]
    },
    {
      "name": "Software",
      "products": [
        {
          "name": "Office license",
          "qty": 4,
          "amount": 396
        },
        {
          "name": "Antivirus",
          "qty": 3,
          "amount": 135
        },
        {
          "name": "Cloud backup",
          "qty": 1,
          "amount": 60
        }
      ]
    },
    {
      "name": "Services",
      "products": [
        {
          "name": "Installation",
          "qty": 1,
          "amount": 50
        },
        {
          "name": "Training",
          "qty": 1,
          "amount": 30
        },
        {
          "name": "Maintenance",
          "qty": 1,
          "amount": 9
        }
      ]
    }
  ]
}
template.xlsxDownload
XLSX template with a loop of categories containing a loop of products
Cells of the first sheet of template.xlsx
RowABC
1{d.title}
3PRODUCTQUANTITYAMOUNT
4{d.categories[i].name}{d.categories[i].products[].amount:aggSum:formatN}
5{d.categories[i].products[i].name}{d.categories[i].products[i].qty:formatN}{d.categories[i].products[i].amount:formatN}
6{d.categories[i].products[i+1].name}
7{d.categories[i+1].name}
report.xlsxDownload
Excel report grouped by category with a subtotal per category
Cells of the first sheet of report.xlsx
RowABC
1Q3 2026 sales by category
3PRODUCTQUANTITYAMOUNT
4Hardware20460
5Laptop1211400
627-inch monitor256000
7Keyboard602100
8Mouse80960
9Software591
10Office license4396
11Antivirus3135
12Cloud backup160
13Services89
14Installation150
15Training130
16Maintenance19

Horizontal loops

Write the [i] and [i+1] tags in two adjacent columns to repeat columns instead of rows. Each row of the loop needs its own [i+1] tag. Learn more about horizontal loops.

data.jsonDownload
{
  "title": "Q3 2026 sales by month",
  "months": [
    {
      "name": "July",
      "orders": 120,
      "amount": 6840
    },
    {
      "name": "August",
      "orders": 98,
      "amount": 5320
    },
    {
      "name": "September",
      "orders": 143,
      "amount": 8980
    }
  ]
}
template.xlsxDownload
XLSX template with a horizontal loop on columns
Cells of the first sheet of template.xlsx
RowABC
1{d.title}
3MONTH{d.months[i].name}{d.months[i+1]}
4Orders{d.months[i].orders:formatN}{d.months[i+1]}
5Amount{d.months[i].amount:formatN}{d.months[i+1]}
report.xlsxDownload
Excel report with one column per month
Cells of the first sheet of report.xlsx
RowABCD
1Q3 2026 sales by month
3MONTHJulyAugustSeptember
4Orders12098143
5Amount684053208980

Numbers and dates

A cell becomes a native Excel number when it contains a single tag ending with :formatN, like {d.amount:formatN}. Carbone writes the raw value, and the number format of the cell displays it. Excel can then sum, sort and chart it. The decimals come from the number format, not from the precision argument.

Other formatters write text, formatted for the language of the report: :formatC for currencies, :formatN(2, acc) for accounting amounts like (1,234.50), and :formatD for dates. To keep an accounting amount as a number, use :formatN with the cell format #,##0.00;(#,##0.00).

data.jsonDownload
{
  "amount": 1234.5,
  "balance": -1234.5,
  "rate": 0.125,
  "date": "2026-11-10"
}
template.xlsxDownload
XLSX template with formatN tags and Excel number formats
Cells of the first sheet of template.xlsx
RowABC
1Number formats
3EXAMPLECELL VALUECELL NUMBER FORMAT
4Amount{d.amount:formatN}$#,##0.00
5Negative balance{d.balance:formatN}$#,##0.00;[Red]($#,##0.00)
6Rate{d.rate:formatN}0.0%
7Amount as text{d.amount:formatC}General (text)
8Accounting as text{d.balance:formatN(2, acc)}General (text)
9Date as text{d.date:formatD('MMMM D, YYYY')}General (text)
report.xlsxDownload
Native Excel numbers next to formatted text values
Cells of the first sheet of report.xlsx
RowABC
1Number formats
3EXAMPLECELL VALUECELL NUMBER FORMAT
4Amount1234.5$#,##0.00
5Negative balance-1234.5$#,##0.00;[Red]($#,##0.00)
6Rate0.1250.0%
7Amount as text$1,234.50General (text)
8Accounting as text(1,234.50)General (text)
9Date as textNovember 10, 2026General (text)

To write :formatN values as text instead, add {o.exportFormattedValuesAsText=true} to the template. Learn more about the number, currency and date formatters.

Conditions

Conditions print a value or remove a row based on the data:

data.jsonDownload
{
  "title": "Q3 2026 sales",
  "total": 21140,
  "products": [
    {
      "name": "Laptop",
      "category": "Hardware",
      "qty": 12,
      "price": 950,
      "amount": 11400,
      "status": "In stock"
    },
    {
      "name": "27-inch monitor",
      "category": "Hardware",
      "qty": 25,
      "price": 240,
      "amount": 6000,
      "status": "Out of stock"
    },
    {
      "name": "Keyboard",
      "category": "Hardware",
      "qty": 60,
      "price": 35,
      "amount": 2100,
      "status": "In stock"
    },
    {
      "name": "Mouse",
      "category": "Hardware",
      "qty": 80,
      "price": 12,
      "amount": 960,
      "status": "Backorder"
    },
    {
      "name": "Office license",
      "category": "Software",
      "qty": 4,
      "price": 99,
      "amount": 396,
      "status": "In stock"
    },
    {
      "name": "Antivirus",
      "category": "Software",
      "qty": 0,
      "price": 45,
      "amount": 135,
      "status": "Out of stock"
    },
    {
      "name": "Cloud backup",
      "category": "Software",
      "qty": 1,
      "price": 60,
      "amount": 60,
      "status": "In stock"
    },
    {
      "name": "Installation",
      "category": "Services",
      "qty": 1,
      "price": 50,
      "amount": 50,
      "status": "Backorder"
    },
    {
      "name": "Training",
      "category": "Services",
      "qty": 1,
      "price": 30,
      "amount": 30,
      "status": "In stock"
    },
    {
      "name": "Maintenance",
      "category": "Services",
      "qty": 0,
      "price": 9,
      "amount": 9,
      "status": "Out of stock"
    }
  ]
}
template.xlsxDownload
XLSX template with an inline condition and a drop row condition
Cells of the first sheet of template.xlsx
RowABC
1{d.title}
3PRODUCTQUANTITYALERT
4{d.products[i].name}{d.products[i].qty:ifEQ(0):drop(row)}{d.products[i].qty:formatN}{d.products[i].qty:ifLT(5):show('Reorder'):elseShow('OK')}
5{d.products[i+1].name}
report.xlsxDownload
Excel report without the products out of stock
Cells of the first sheet of report.xlsx
RowABC
1Q3 2026 sales
3PRODUCTQUANTITYALERT
4Laptop12OK
527-inch monitor25OK
6Keyboard60OK
7Mouse80OK
8Office license4Reorder
9Cloud backup1Reorder
10Installation1Reorder
11Training1Reorder

Learn more about inline conditions and smart conditions.

Totals

Aggregators compute totals without Excel formulas. :aggSum sums a column, :aggSum(.category) sums each category, and :cumSum prints a running total. Add :formatN to get native numbers. Totals match the rows after sorting or filtering, and stay correct in PDF and CSV outputs.

data.jsonDownload
{
  "title": "Q3 2026 sales",
  "total": 21140,
  "products": [
    { "name": "Laptop",          "category": "Hardware", "qty": 12, "price": 950, "amount": 11400, "status": "In stock" },
    { "name": "27-inch monitor", "category": "Hardware", "qty": 25, "price": 240, "amount": 6000,  "status": "Out of stock" },
    { "name": "Keyboard",        "category": "Hardware", "qty": 60, "price": 35,  "amount": 2100,  "status": "In stock" },
    { "name": "Mouse",           "category": "Hardware", "qty": 80, "price": 12,  "amount": 960,   "status": "Backorder" },
    { "name": "Office license",  "category": "Software", "qty": 4,  "price": 99,  "amount": 396,   "status": "In stock" },
    { "name": "Antivirus",       "category": "Software", "qty": 3,  "price": 45,  "amount": 135,   "status": "Out of stock" },
    { "name": "Cloud backup",    "category": "Software", "qty": 1,  "price": 60,  "amount": 60,    "status": "In stock" },
    { "name": "Installation",    "category": "Services", "qty": 1,  "price": 50,  "amount": 50,    "status": "Backorder" },
    { "name": "Training",        "category": "Services", "qty": 1,  "price": 30,  "amount": 30,    "status": "In stock" },
    { "name": "Maintenance",     "category": "Services", "qty": 1,  "price": 9,   "amount": 9,     "status": "Out of stock" }
  ]
}
template.xlsxDownload
XLSX template with aggSum and cumSum aggregators
Cells of the first sheet of template.xlsx
RowABCDE
1{d.title}
3PRODUCTCATEGORYAMOUNTRUNNING TOTALCATEGORY TOTAL
4{d.products[i].name}{d.products[i].category}{d.products[i].amount:formatN}{d.products[i].amount:cumSum:formatN}{d.products[i].amount:aggSum(.category):formatN}
5{d.products[i+1].name}
6Total{d.products[].amount:aggSum:formatN}
report.xlsxDownload
Excel report with running totals and totals per category
Cells of the first sheet of report.xlsx
RowABCDE
1Q3 2026 sales
3PRODUCTCATEGORYAMOUNTRUNNING TOTALCATEGORY TOTAL
4LaptopHardware114001140020460
527-inch monitorHardware60001740020460
6KeyboardHardware21001950020460
7MouseHardware9602046020460
8Office licenseSoftware39620856591
9AntivirusSoftware13520991591
10Cloud backupSoftware6021051591
11InstallationServices502110189
12TrainingServices302113189
13MaintenanceServices92114089
14Total21140

Aggregators are an Enterprise feature. Learn more about aggregators.

Formulas

PRE-RELEASE v5.18.0+ Add {o.preReleaseFeatureIn=5018000} to the template. Without it, formulas, merged cells, conditional formatting, data validations, tables, charts, pivot tables, hyperlinks, named ranges, the print area and page breaks do not follow the rows added by loops. Learn more

Carbone updates formulas the way Excel does when you insert rows:

Excel recalculates the workbook when you open the file.

data.jsonDownload
{
  "title": "Q3 2026 sales",
  "total": 21140,
  "products": [
    { "name": "Laptop",          "category": "Hardware", "qty": 12, "price": 950, "amount": 11400, "status": "In stock" },
    { "name": "27-inch monitor", "category": "Hardware", "qty": 25, "price": 240, "amount": 6000,  "status": "Out of stock" },
    { "name": "Keyboard",        "category": "Hardware", "qty": 60, "price": 35,  "amount": 2100,  "status": "In stock" },
    { "name": "Mouse",           "category": "Hardware", "qty": 80, "price": 12,  "amount": 960,   "status": "Backorder" },
    { "name": "Office license",  "category": "Software", "qty": 4,  "price": 99,  "amount": 396,   "status": "In stock" },
    { "name": "Antivirus",       "category": "Software", "qty": 3,  "price": 45,  "amount": 135,   "status": "Out of stock" },
    { "name": "Cloud backup",    "category": "Software", "qty": 1,  "price": 60,  "amount": 60,    "status": "In stock" },
    { "name": "Installation",    "category": "Services", "qty": 1,  "price": 50,  "amount": 50,    "status": "Backorder" },
    { "name": "Training",        "category": "Services", "qty": 1,  "price": 30,  "amount": 30,    "status": "In stock" },
    { "name": "Maintenance",     "category": "Services", "qty": 1,  "price": 9,   "amount": 9,     "status": "Out of stock" }
  ]
}
template.xlsxDownload
XLSX template with Excel formulas in the repeated row and in the total
Cells of the first sheet of template.xlsx
RowABCDEF
1{d.title}
2{o.preReleaseFeatureIn=5018000}
3PRODUCTQUANTITYPRICEAMOUNTINCL. VATRUNNING TOTAL
4{d.products[i].name}{d.products[i].qty:formatN}{d.products[i].price:formatN}=B4*C4=D4*(1+$B$9)=SUM($D$4:D4)
5{d.products[i+1].name}{d.products[i+1].qty:formatN}{d.products[i+1].price:formatN}=B5*C5=D5*(1+$B$9)=SUM($D$4:D5)
6Total=SUM(D4:D5)=SUM(E4:E5)
9VAT rate0.2
report.xlsxDownload
Excel formulas computed on every generated row
Cells of the first sheet of report.xlsx
RowABCDEF
1Q3 2026 sales
3PRODUCTQUANTITYPRICEAMOUNTINCL. VATRUNNING TOTAL
4Laptop12950=B4*C4=D4*(1+$B$17)=SUM($D$4:D4)
527-inch monitor25240=B5*C5=D5*(1+$B$17)=SUM($D$4:D5)
6Keyboard6035=B6*C6=D6*(1+$B$17)=SUM($D$4:D6)
7Mouse8012=B7*C7=D7*(1+$B$17)=SUM($D$4:D7)
8Office license499=B8*C8=D8*(1+$B$17)=SUM($D$4:D8)
9Antivirus345=B9*C9=D9*(1+$B$17)=SUM($D$4:D9)
10Cloud backup160=B10*C10=D10*(1+$B$17)=SUM($D$4:D10)
11Installation150=B11*C11=D11*(1+$B$17)=SUM($D$4:D11)
12Training130=B12*C12=D12*(1+$B$17)=SUM($D$4:D12)
13Maintenance19=B13*C13=D13*(1+$B$17)=SUM($D$4:D13)
14Total=SUM(D4:D13)=SUM(E4:E13)
17VAT rate0.2

Named ranges cover the generated rows too. Carbone does not change formulas that contain Carbone tags or use INDIRECT.

Merged cells

PRE-RELEASE v5.18.0+ Add {o.preReleaseFeatureIn=5018000} to the template. A sheet that uses :mergeCell gets this feature without the tag.

Merged cells in the repeated row are copied to each row, like the product in columns A and B. Merged cells below the loop move down, like the total.

:mergeCell merges the consecutive cells of a column that have the same value. {d.products[i].category:mergeCell} prints each category once, in one cell merged over its products.

data.jsonDownload
{
  "title": "Q3 2026 sales",
  "total": 21140,
  "products": [
    { "name": "Laptop",          "category": "Hardware", "qty": 12, "price": 950, "amount": 11400, "status": "In stock" },
    { "name": "27-inch monitor", "category": "Hardware", "qty": 25, "price": 240, "amount": 6000,  "status": "Out of stock" },
    { "name": "Keyboard",        "category": "Hardware", "qty": 60, "price": 35,  "amount": 2100,  "status": "In stock" },
    { "name": "Mouse",           "category": "Hardware", "qty": 80, "price": 12,  "amount": 960,   "status": "Backorder" },
    { "name": "Office license",  "category": "Software", "qty": 4,  "price": 99,  "amount": 396,   "status": "In stock" },
    { "name": "Antivirus",       "category": "Software", "qty": 3,  "price": 45,  "amount": 135,   "status": "Out of stock" },
    { "name": "Cloud backup",    "category": "Software", "qty": 1,  "price": 60,  "amount": 60,    "status": "In stock" },
    { "name": "Installation",    "category": "Services", "qty": 1,  "price": 50,  "amount": 50,    "status": "Backorder" },
    { "name": "Training",        "category": "Services", "qty": 1,  "price": 30,  "amount": 30,    "status": "In stock" },
    { "name": "Maintenance",     "category": "Services", "qty": 1,  "price": 9,   "amount": 9,     "status": "Out of stock" }
  ]
}
template.xlsxDownload
XLSX template with merged cells and the mergeCell formatter
Cells of the first sheet of template.xlsx
RowABCDE
1{d.title}
3PRODUCTCATEGORYQUANTITYAMOUNT
4{d.products[i].name}{d.products[i].category:mergeCell}{d.products[i].qty:formatN}{d.products[i].amount:formatN}
5{d.products[i+1].name}{d.products[i+1].category}{d.products[i+1].qty:formatN}{d.products[i+1].amount:formatN}
6Total{d.total:formatN}
report.xlsxDownload
Excel categories merged vertically by Carbone
Cells of the first sheet of report.xlsx
RowABCDE
1Q3 2026 sales
3PRODUCTCATEGORYQUANTITYAMOUNT
4LaptopHardware1211400
527-inch monitor256000
6Keyboard602100
7Mouse80960
8Office licenseSoftware4396
9Antivirus3135
10Cloud backup160
11InstallationServices150
12Training130
13Maintenance19
14Total21140

Conditional formatting

PRE-RELEASE v5.18.0+ Add {o.preReleaseFeatureIn=5018000} to the template.

Define the rules on the repeated row, and Carbone extends them to every generated row. In this example, quantities above 50 are green, a formula rule highlights out-of-stock products, prices get a color scale, and amounts get data bars. The rule on the total moves with the total.

data.jsonDownload
{
  "title": "Q3 2026 sales",
  "total": 21140,
  "products": [
    { "name": "Laptop",          "category": "Hardware", "qty": 12, "price": 950, "amount": 11400, "status": "In stock" },
    { "name": "27-inch monitor", "category": "Hardware", "qty": 25, "price": 240, "amount": 6000,  "status": "Out of stock" },
    { "name": "Keyboard",        "category": "Hardware", "qty": 60, "price": 35,  "amount": 2100,  "status": "In stock" },
    { "name": "Mouse",           "category": "Hardware", "qty": 80, "price": 12,  "amount": 960,   "status": "Backorder" },
    { "name": "Office license",  "category": "Software", "qty": 4,  "price": 99,  "amount": 396,   "status": "In stock" },
    { "name": "Antivirus",       "category": "Software", "qty": 3,  "price": 45,  "amount": 135,   "status": "Out of stock" },
    { "name": "Cloud backup",    "category": "Software", "qty": 1,  "price": 60,  "amount": 60,    "status": "In stock" },
    { "name": "Installation",    "category": "Services", "qty": 1,  "price": 50,  "amount": 50,    "status": "Backorder" },
    { "name": "Training",        "category": "Services", "qty": 1,  "price": 30,  "amount": 30,    "status": "In stock" },
    { "name": "Maintenance",     "category": "Services", "qty": 1,  "price": 9,   "amount": 9,     "status": "Out of stock" }
  ]
}
template.xlsxDownload
XLSX template with conditional formatting rules on the repeated row
Cells of the first sheet of template.xlsx
RowABCDEF
1{d.title}
2{o.preReleaseFeatureIn=5018000}
3PRODUCTCATEGORYQUANTITYPRICEAMOUNTSTATUS
4{d.products[i].name}{d.products[i].category}{d.products[i].qty:formatN}{d.products[i].price:formatN}{d.products[i].amount:formatN}{d.products[i].status}
5{d.products[i+1].name}
6Total{d.total:formatN}
report.xlsxDownload
Excel conditional formatting, color scale and data bars on every row
Cells of the first sheet of report.xlsx
RowABCDEF
1Q3 2026 sales
3PRODUCTCATEGORYQUANTITYPRICEAMOUNTSTATUS
4LaptopHardware1295011400In stock
527-inch monitorHardware252406000Out of stock
6KeyboardHardware60352100In stock
7MouseHardware8012960Backorder
8Office licenseSoftware499396In stock
9AntivirusSoftware345135Out of stock
10Cloud backupSoftware16060In stock
11InstallationServices15050Backorder
12TrainingServices13030In stock
13MaintenanceServices199Out of stock
14Total21140

Tables and filters

PRE-RELEASE v5.18.0+ Add {o.preReleaseFeatureIn=5018000} to the template.

An Excel table (Insert > Table) on the loop grows with the data. Banded rows, filter buttons and structured references like =SUM(Sales[Amount]) cover all the generated rows.

data.jsonDownload
{
  "title": "Q3 2026 sales",
  "total": 21140,
  "products": [
    { "name": "Laptop",          "category": "Hardware", "qty": 12, "price": 950, "amount": 11400, "status": "In stock" },
    { "name": "27-inch monitor", "category": "Hardware", "qty": 25, "price": 240, "amount": 6000,  "status": "Out of stock" },
    { "name": "Keyboard",        "category": "Hardware", "qty": 60, "price": 35,  "amount": 2100,  "status": "In stock" },
    { "name": "Mouse",           "category": "Hardware", "qty": 80, "price": 12,  "amount": 960,   "status": "Backorder" },
    { "name": "Office license",  "category": "Software", "qty": 4,  "price": 99,  "amount": 396,   "status": "In stock" },
    { "name": "Antivirus",       "category": "Software", "qty": 3,  "price": 45,  "amount": 135,   "status": "Out of stock" },
    { "name": "Cloud backup",    "category": "Software", "qty": 1,  "price": 60,  "amount": 60,    "status": "In stock" },
    { "name": "Installation",    "category": "Services", "qty": 1,  "price": 50,  "amount": 50,    "status": "Backorder" },
    { "name": "Training",        "category": "Services", "qty": 1,  "price": 30,  "amount": 30,    "status": "In stock" },
    { "name": "Maintenance",     "category": "Services", "qty": 1,  "price": 9,   "amount": 9,     "status": "Out of stock" }
  ]
}
template.xlsxDownload
XLSX template with an Excel table named Sales on the loop
Cells of the first sheet of template.xlsx
RowABCDEF
1{d.title}
2{o.preReleaseFeatureIn=5018000}
3ProductCategoryQuantityPriceAmountStatus
4{d.products[i].name}{d.products[i].category}{d.products[i].qty:formatN}{d.products[i].price:formatN}{d.products[i].amount:formatN}{d.products[i].status}
5{d.products[i+1].name}
8Table total=SUM(Sales[Amount])
report.xlsxDownload
Excel table with banded rows extended to all products
Cells of the first sheet of report.xlsx
RowABCDEF
1Q3 2026 sales
3ProductCategoryQuantityPriceAmountStatus
4LaptopHardware1295011400In stock
527-inch monitorHardware252406000Out of stock
6KeyboardHardware60352100In stock
7MouseHardware8012960Backorder
8Office licenseSoftware499396In stock
9AntivirusSoftware345135Out of stock
10Cloud backupSoftware16060In stock
11InstallationServices15050Backorder
12TrainingServices13030In stock
13MaintenanceServices199Out of stock
16Table total=SUM(Sales[Amount])

AutoFilters and data validation rules (drop-down lists, whole numbers...) on the repeated row extend the same way. When the source of a drop-down list is below the loop, the list follows the source cells as they move down.

Charts

PRE-RELEASE v5.18.0+ Add {o.preReleaseFeatureIn=5018000} to the template.

A native Excel chart whose series use the cells of the loop shows all the generated rows. A chart placed below the loop moves down with the rows.

data.jsonDownload
{
  "title": "Q3 2026 sales",
  "total": 21140,
  "products": [
    { "name": "Laptop",          "category": "Hardware", "qty": 12, "price": 950, "amount": 11400, "status": "In stock" },
    { "name": "27-inch monitor", "category": "Hardware", "qty": 25, "price": 240, "amount": 6000,  "status": "Out of stock" },
    { "name": "Keyboard",        "category": "Hardware", "qty": 60, "price": 35,  "amount": 2100,  "status": "In stock" },
    { "name": "Mouse",           "category": "Hardware", "qty": 80, "price": 12,  "amount": 960,   "status": "Backorder" },
    { "name": "Office license",  "category": "Software", "qty": 4,  "price": 99,  "amount": 396,   "status": "In stock" },
    { "name": "Antivirus",       "category": "Software", "qty": 3,  "price": 45,  "amount": 135,   "status": "Out of stock" },
    { "name": "Cloud backup",    "category": "Software", "qty": 1,  "price": 60,  "amount": 60,    "status": "In stock" },
    { "name": "Installation",    "category": "Services", "qty": 1,  "price": 50,  "amount": 50,    "status": "Backorder" },
    { "name": "Training",        "category": "Services", "qty": 1,  "price": 30,  "amount": 30,    "status": "In stock" },
    { "name": "Maintenance",     "category": "Services", "qty": 1,  "price": 9,   "amount": 9,     "status": "Out of stock" }
  ]
}
template.xlsxDownload
XLSX template with a column chart and a pie chart on the loop cells
Cells of the first sheet of template.xlsx
RowABCDEF
1{d.title}
2{o.preReleaseFeatureIn=5018000}
3PRODUCTCATEGORYQUANTITYPRICEAMOUNTSTATUS
4{d.products[i].name}{d.products[i].category}{d.products[i].qty:formatN}{d.products[i].price:formatN}{d.products[i].amount:formatN}{d.products[i].status}
5{d.products[i+1].name}
6Total{d.total:formatN}
report.xlsxDownload
Excel charts showing the 10 products generated by Carbone
Cells of the first sheet of report.xlsx
RowABCDEF
1Q3 2026 sales
3PRODUCTCATEGORYQUANTITYPRICEAMOUNTSTATUS
4LaptopHardware1295011400In stock
527-inch monitorHardware252406000Out of stock
6KeyboardHardware60352100In stock
7MouseHardware8012960Backorder
8Office licenseSoftware499396In stock
9AntivirusSoftware345135Out of stock
10Cloud backupSoftware16060In stock
11InstallationServices15050Backorder
12TrainingServices13030In stock
13MaintenanceServices199Out of stock
14Total21140

To draw a chart image from the data instead, use the :chart formatter.

Pivot tables

PRE-RELEASE v5.18.0+ Add {o.preReleaseFeatureIn=5018000} to the template.

Build the pivot table from the cells of the loop. In this example, it is on another sheet. Its source covers all the generated rows, and Excel refreshes it when you open the file.

data.jsonDownload
{
  "title": "Q3 2026 sales",
  "total": 21140,
  "products": [
    { "name": "Laptop",          "category": "Hardware", "qty": 12, "price": 950, "amount": 11400, "status": "In stock" },
    { "name": "27-inch monitor", "category": "Hardware", "qty": 25, "price": 240, "amount": 6000,  "status": "Out of stock" },
    { "name": "Keyboard",        "category": "Hardware", "qty": 60, "price": 35,  "amount": 2100,  "status": "In stock" },
    { "name": "Mouse",           "category": "Hardware", "qty": 80, "price": 12,  "amount": 960,   "status": "Backorder" },
    { "name": "Office license",  "category": "Software", "qty": 4,  "price": 99,  "amount": 396,   "status": "In stock" },
    { "name": "Antivirus",       "category": "Software", "qty": 3,  "price": 45,  "amount": 135,   "status": "Out of stock" },
    { "name": "Cloud backup",    "category": "Software", "qty": 1,  "price": 60,  "amount": 60,    "status": "In stock" },
    { "name": "Installation",    "category": "Services", "qty": 1,  "price": 50,  "amount": 50,    "status": "Backorder" },
    { "name": "Training",        "category": "Services", "qty": 1,  "price": 30,  "amount": 30,    "status": "In stock" },
    { "name": "Maintenance",     "category": "Services", "qty": 1,  "price": 9,   "amount": 9,     "status": "Out of stock" }
  ]
}
template.xlsxDownload
XLSX template with the source data of a pivot table
template.xlsx · Pivot
XLSX template with the source data of a pivot table, Pivot sheet
Cells of the first sheet of template.xlsx
RowABCDEF
1{d.title}
2{o.preReleaseFeatureIn=5018000}
3ProductCategoryQuantityPriceAmountStatus
4{d.products[i].name}{d.products[i].category}{d.products[i].qty:formatN}{d.products[i].price:formatN}{d.products[i].amount:formatN}{d.products[i].status}
5{d.products[i+1].name}
6Total{d.total:formatN}
report.xlsx · PivotDownload
Excel pivot table with the sales of each category, Pivot sheet
Cells of the first sheet of report.xlsx
RowABCDEF
1Q3 2026 sales
3ProductCategoryQuantityPriceAmountStatus
4LaptopHardware1295011400In stock
527-inch monitorHardware252406000Out of stock
6KeyboardHardware60352100In stock
7MouseHardware8012960Backorder
8Office licenseSoftware499396In stock
9AntivirusSoftware345135Out of stock
10Cloud backupSoftware16060In stock
11InstallationServices15050Backorder
12TrainingServices13030In stock
13MaintenanceServices199Out of stock
14Total21140

Until the pivot table is refreshed, the file keeps the pivot values of the template. An application that does not refresh pivot tables when it opens a file shows these template values.

PRE-RELEASE v5.18.0+ Add {o.preReleaseFeatureIn=5018000} to the template.

A link in the repeated row is copied to each row. A link to a cell of the workbook, like "Go to total", still targets that cell after the rows are added. A link below the loop moves with its cell.

data.jsonDownload
{
  "title": "Q3 2026 sales",
  "total": 21140,
  "products": [
    { "name": "Laptop",          "category": "Hardware", "qty": 12, "price": 950, "amount": 11400, "status": "In stock" },
    { "name": "27-inch monitor", "category": "Hardware", "qty": 25, "price": 240, "amount": 6000,  "status": "Out of stock" },
    { "name": "Keyboard",        "category": "Hardware", "qty": 60, "price": 35,  "amount": 2100,  "status": "In stock" },
    { "name": "Mouse",           "category": "Hardware", "qty": 80, "price": 12,  "amount": 960,   "status": "Backorder" },
    { "name": "Office license",  "category": "Software", "qty": 4,  "price": 99,  "amount": 396,   "status": "In stock" },
    { "name": "Antivirus",       "category": "Software", "qty": 3,  "price": 45,  "amount": 135,   "status": "Out of stock" },
    { "name": "Cloud backup",    "category": "Software", "qty": 1,  "price": 60,  "amount": 60,    "status": "In stock" },
    { "name": "Installation",    "category": "Services", "qty": 1,  "price": 50,  "amount": 50,    "status": "Backorder" },
    { "name": "Training",        "category": "Services", "qty": 1,  "price": 30,  "amount": 30,    "status": "In stock" },
    { "name": "Maintenance",     "category": "Services", "qty": 1,  "price": 9,   "amount": 9,     "status": "Out of stock" }
  ]
}
template.xlsxDownload
XLSX template with hyperlinks on the repeated row and below the loop
Cells of the first sheet of template.xlsx
RowABCDEF
1{d.title}
2Go to total{o.preReleaseFeatureIn=5018000}
3PRODUCTCATEGORYQUANTITYPRICEAMOUNTSTATUS
4{d.products[i].name}{d.products[i].category}{d.products[i].qty:formatN}{d.products[i].price:formatN}{d.products[i].amount:formatN}{d.products[i].status}
5{d.products[i+1].name}
6Total{d.total:formatN}
8Documentation
report.xlsxDownload
Excel hyperlinks on every product row
Cells of the first sheet of report.xlsx
RowABCDEF
1Q3 2026 sales
2Go to total
3PRODUCTCATEGORYQUANTITYPRICEAMOUNTSTATUS
4LaptopHardware1295011400In stock
527-inch monitorHardware252406000Out of stock
6KeyboardHardware60352100In stock
7MouseHardware8012960Backorder
8Office licenseSoftware499396In stock
9AntivirusSoftware345135Out of stock
10Cloud backupSoftware16060In stock
11InstallationServices15050Backorder
12TrainingServices13030In stock
13MaintenanceServices199Out of stock
14Total21140
16Documentation

To build the address of a link from the data, see dynamic hyperlinks.

Pictures

Insert a temporary picture and write the tag in its alt text (right-click > Edit Alt Text), like {d.logo}. Carbone replaces it with the image from the data, given as a public URL or a Base64 data URI. The image is stretched to the size of the temporary picture, so give the temporary picture the same proportions as your images.

data.jsonDownload
{
  "company": "Acme Corporation",
  "manager": "Jane Smith",
  "region": "North America",
  "period": "July to September 2026",
  "logo": "https://carbone.io/img/carbone_icon_v3_github.png"
}
template.xlsxDownload
XLSX template with a logo placeholder
Cells of the first sheet of template.xlsx
RowAB
1{d.company}
3Sales manager{d.manager}
4Region{d.region}
5Period{d.period}
report.xlsxDownload
Excel report with the logo of the data
Cells of the first sheet of report.xlsx
RowAB
1Acme Corporation
3Sales managerJane Smith
4RegionNorth America
5PeriodJuly to September 2026

Loops do not repeat pictures. A picture in the repeated row stays on the first row. With the pre-release tag, pictures and comments below the loop move down with the rows. Learn more about pictures.

Barcodes

Barcodes are pictures too. Write the :barcode formatter in the alt text of a temporary picture. {d.ean:barcode(ean13, scale:6)} draws the EAN-13 code of the product, and {d.url:barcode(qrcode, scale:6)} draws a QR code of its web page.

data.jsonDownload
{
  "name": "Laptop Pro 14",
  "sku": "LAP-14-2026",
  "ean": "4006381333931",
  "price": 950,
  "url": "https://carbone.io/documentation/design/template-formats/xlsx.html"
}
template.xlsxDownload
XLSX template with barcode and QR code placeholders
Cells of the first sheet of template.xlsx
RowAB
1{d.name}
3SKU{d.sku}
4EAN{d.ean}
5Price{d.price:formatN}
report.xlsxDownload
Excel report with an EAN-13 barcode and a QR code
Cells of the first sheet of report.xlsx
RowAB
1Laptop Pro 14
3SKULAP-14-2026
4EAN4006381333931
5Price950

The barcode is stretched to the size of the temporary picture, so keep the proportions of the barcode (square for a QR code). scale sets the resolution, from 1 to 10 (3 by default). Learn more about the barcode types and options.

Translations

Write static text in {t( )} tags, and add the :t formatter to translate values from the data. Carbone uses the translations for the lang of the report. This example uses "lang": "fr-fr" and the dictionary in the Translations tab.

data.jsonDownload
{
  "title": "Q3 2026 sales",
  "total": 21140,
  "products": [
    { "name": "Laptop",          "category": "Hardware", "qty": 12, "price": 950, "amount": 11400, "status": "In stock" },
    { "name": "27-inch monitor", "category": "Hardware", "qty": 25, "price": 240, "amount": 6000,  "status": "Out of stock" },
    { "name": "Keyboard",        "category": "Hardware", "qty": 60, "price": 35,  "amount": 2100,  "status": "In stock" },
    { "name": "Mouse",           "category": "Hardware", "qty": 80, "price": 12,  "amount": 960,   "status": "Backorder" },
    { "name": "Office license",  "category": "Software", "qty": 4,  "price": 99,  "amount": 396,   "status": "In stock" },
    { "name": "Antivirus",       "category": "Software", "qty": 3,  "price": 45,  "amount": 135,   "status": "Out of stock" },
    { "name": "Cloud backup",    "category": "Software", "qty": 1,  "price": 60,  "amount": 60,    "status": "In stock" },
    { "name": "Installation",    "category": "Services", "qty": 1,  "price": 50,  "amount": 50,    "status": "Backorder" },
    { "name": "Training",        "category": "Services", "qty": 1,  "price": 30,  "amount": 30,    "status": "In stock" },
    { "name": "Maintenance",     "category": "Services", "qty": 1,  "price": 9,   "amount": 9,     "status": "Out of stock" }
  ]
}
translations.jsonDownload
{
  "fr-fr": {
    "Quarterly sales": "Ventes du trimestre",
    "Product": "Produit",
    "Category": "Catégorie",
    "Quantity": "Quantité",
    "Status": "Statut",
    "Hardware": "Matériel",
    "Software": "Logiciel",
    "Services": "Services",
    "In stock": "En stock",
    "Out of stock": "Rupture",
    "Backorder": "En commande"
  }
}
template.xlsxDownload
XLSX template with translation tags
Cells of the first sheet of template.xlsx
RowABCD
1{t(Quarterly sales)}
3{t(Product)}{t(Category)}{t(Quantity)}{t(Status)}
4{d.products[i].name}{d.products[i].category:t}{d.products[i].qty:formatN}{d.products[i].status:t}
5{d.products[i+1].name}
report.xlsxDownload
Excel report translated in French by Carbone
Cells of the first sheet of report.xlsx
RowABCD
1Ventes du trimestre
3ProduitCatégorieQuantitéStatut
4LaptopMatériel12En stock
527-inch monitorMatériel25Rupture
6KeyboardMatériel60En stock
7MouseMatériel80En commande
8Office licenseLogiciel4En stock
9AntivirusLogiciel3Rupture
10Cloud backupLogiciel1En stock
11InstallationServices1En commande
12TrainingServices1En stock
13MaintenanceServices1Rupture

Learn more about translations.

Page layout

Tags work in headers and footers (Page Layout > Header & Footer), next to the Excel page number fields. {d.company} in the header prints the company name on every page.

PRE-RELEASE v5.18.0+ With {o.preReleaseFeatureIn=5018000}, the print area covers the generated rows, and a manual page break below the loop moves with its row.

Sheets

Tags work in sheet names, as in cells. Excel limits sheet names to 31 characters. If a value makes the name longer, Excel reports a problem when it opens the file.

XLSX templates cannot create one sheet per item of an array yet. Use an ODS template instead, and convert it to XLSX.

Convert XLSX to PDF

To generate a PDF from an XLSX template with the Carbone API:

  1. Prepare two files: an XLSX template with Carbone tags, like {d.name}, and the JSON data to populate it. You can use the files of the Loops example.
  2. Generate the PDF in one of three ways:
    • Use an official Carbone integration, like N8N, Make, Zapier, Airtable or Bubble.
    • Use a Carbone SDK: Node.js, Go, Python, Java, JavaScript or PHP.
    • Send a POST /render/template HTTP request to the Carbone API, like this curl command:
curl --location --request POST 'https://api.carbone.io/render/template?download=true' \
  --header 'carbone-version: 5' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer API_TOKEN' \
  --data-raw "{
    \"data\": $(cat loops-data.json),
    \"template\": \"$(base64 -i loops-template.xlsx)\",
    \"convertTo\": \"pdf\"
  }" \
  --output report.pdf
💡 Linux users: replace base64 -i loops-template.xlsx with base64 -w 0 loops-template.xlsx

Key parameters

Parameter Description
template Your XLSX template, encoded as a Base64 string.
data JSON data used to populate the template.
convertTo "pdf", or another output format: "xlsx" (by default), "ods", "csv"...
converter Optional: LibreOffice "L" (default) or OnlyOffice "O". See converters.
?download=true Query parameter to download the document as a stream.
Authorization Header required for the Carbone Cloud API. Get your API key here.

Each sheet is printed with its own page setup: print area, orientation, scaling, headers and footers. For more details, refer to the Carbone API Documentation.