Skip to content

Examples

The examples/xlfill-test project in the repository exercises every XLFill feature in one Go program. Each test creates a template, fills it, and verifies the output.

You can run it yourself:

Terminal window
cd examples/xlfill-test
go run .

Or just browse the files below. Open any input file in Excel or LibreOffice to see the jx: comments and ${...} expressions. Then open the matching output file to see the filled result.

#FeatureCommand / APITemplateOutputSource
01Basic loopjx:eacht01.xlsx01_basic_each.xlsxcode
02Loop indexjx:each varIndext02.xlsx02_varindex.xlsxcode
03Expand RIGHTjx:each directiont03.xlsx03_direction_right.xlsxcode
04Filter itemsjx:each selectt04.xlsx04_select.xlsxcode
05Sort itemsjx:each orderByt05.xlsx05_orderby.xlsxcode
06Group itemsjx:each groupByt06.xlsx06_groupby.xlsxcode
07Conditionaljx:ift07.xlsx07_if_command.xlsxcode
08FormulasFormula expansiont08.xlsx08_formulas.xlsxcode
09Dynamic gridjx:gridt09.xlsx09_grid.xlsxcode
10Embed imagejx:imaget10.xlsx10_image.xlsxcode
11Merge cellsjx:mergeCellst11.xlsx11_mergecells.xlsxcode
12Hyperlinkshyperlink() expressiont12.xlsx12_hyperlinks.xlsxcode
13Nested loopsNested jx:eacht13.xlsx13_nested_each.xlsxcode
14Multi-sheetjx:each multisheett14.xlsx14_multisheet.xlsxcode
15Custom notationWithExpressionNotationt15.xlsx15_custom_notation.xlsxcode
16Keep template sheetWithKeepTemplateSheett16.xlsx16_keep_template.xlsxcode
17Auto row heightjx:autoRowHeightt17.xlsx17_autorowheight.xlsxcode
18FillBytes APIxlfill.FillBytest18.xlsx18_fill_bytes.xlsxcode
19FillReader APIxlfill.FillReadert19.xlsx19_fill_reader.xlsxcode

Template cells: A1:"Name" B1:"Age" C1:"Salary" A2:"${e.Name}" B2:"${e.Age}" C2:"${e.Salary}"

Cell A1 comment: jx:area(lastCell="C2")
Cell A2 comment: jx:each(items="employees" var="e" lastCell="C2")
data := map[string]any{
"employees": []any{
map[string]any{"Name": "Alice", "Age": 30, "Salary": 5000},
map[string]any{"Name": "Bob", "Age": 25, "Salary": 6000},
map[string]any{"Name": "Carol", "Age": 35, "Salary": 7000},
},
}
xlfill.Fill("input/t01.xlsx", "output/01_basic_each.xlsx", data)

Template cells: A1:"#" B1:"Item" A2:"${idx + 1}" B2:"${e}"

Cell A1 comment: jx:area(lastCell="B2")
Cell A2 comment: jx:each(items="items" var="e" varIndex="idx" lastCell="B2")
data := map[string]any{"items": []any{"Apple", "Banana", "Cherry"}}
xlfill.Fill("input/t02.xlsx", "output/02_varindex.xlsx", data)

Template cells: A1:"${e}"

Cell A1 comment:
jx:area(lastCell="A1")
jx:each(items="months" var="e" direction="RIGHT" lastCell="A1")
data := map[string]any{"months": []any{"Jan", "Feb", "Mar", "Apr"}}
xlfill.Fill("input/t03.xlsx", "output/03_direction_right.xlsx", data)

Template cells: A1:"Name" B1:"Salary" A2:"${e.Name}" B2:"${e.Salary}"

Cell A1 comment: jx:area(lastCell="B2")
Cell A2 comment: jx:each(items="employees" var="e" select="e.Salary >= 6000" lastCell="B2")
data := map[string]any{
"employees": []any{
map[string]any{"Name": "Alice", "Salary": 5000},
map[string]any{"Name": "Bob", "Salary": 6000},
map[string]any{"Name": "Carol", "Salary": 7000},
},
}
// Output contains only Bob and Carol
xlfill.Fill("input/t04.xlsx", "output/04_select.xlsx", data)

Template cells: A1:"Name" A2:"${e.Name}"

Cell A1 comment: jx:area(lastCell="A2")
Cell A2 comment: jx:each(items="names" var="e" orderBy="e.Name DESC" lastCell="A2")
data := map[string]any{
"names": []any{
map[string]any{"Name": "Charlie"},
map[string]any{"Name": "Alice"},
map[string]any{"Name": "Bob"},
},
}
// Output order: Charlie, Bob, Alice
xlfill.Fill("input/t05.xlsx", "output/05_orderby.xlsx", data)

Template cells: A1:"${g.Item.Department}" B1:"${g.Item.Name}"

Cell A1 comment:
jx:area(lastCell="B1")
jx:each(items="employees" var="g" groupBy="g.Department" lastCell="B1")
data := map[string]any{
"employees": []any{
map[string]any{"Name": "Alice", "Department": "Engineering"},
map[string]any{"Name": "Bob", "Department": "Sales"},
map[string]any{"Name": "Carol", "Department": "Engineering"},
},
}
// Output: 2 rows — Engineering, Sales
xlfill.Fill("input/t06.xlsx", "output/06_groupby.xlsx", data)

Template cells: A1:"Name" B1:"Status" A2:"${e.Name}" B2:"ACTIVE"

Cell A1 comment: jx:area(lastCell="B2")
Cell A2 comment: jx:each(items="employees" var="e" lastCell="B2")
Cell B2 comment: jx:if(condition="e.Active" lastCell="B2")
data := map[string]any{
"employees": []any{
map[string]any{"Name": "Alice", "Active": true},
map[string]any{"Name": "Bob", "Active": false},
map[string]any{"Name": "Carol", "Active": true},
},
}
xlfill.Fill("input/t07.xlsx", "output/07_if_command.xlsx", data)

Template cells: A1:"Amount" A2:"${e.Amount}" A3: =SUM(A2:A2)

Cell A1 comment: jx:area(lastCell="A3")
Cell A2 comment: jx:each(items="items" var="e" lastCell="A2")
data := map[string]any{
"items": []any{
map[string]any{"Amount": 100},
map[string]any{"Amount": 200},
map[string]any{"Amount": 300},
},
}
// SUM formula auto-expands to =SUM(A2:A4)
xlfill.Fill("input/t08.xlsx", "output/08_formulas.xlsx", data)

Template cells: A1:"placeholder"

Cell A1 comment:
jx:area(lastCell="A2")
jx:grid(headers="headers" data="data" lastCell="A2")
data := map[string]any{
"headers": []any{"Name", "Age", "City"},
"data": []any{
[]any{"Alice", 30, "NYC"},
[]any{"Bob", 25, "LA"},
},
}
xlfill.Fill("input/t09.xlsx", "output/09_grid.xlsx", data)

Template cells: A1:"Logo below" A2:""

Cell A1 comment: jx:area(lastCell="A2")
Cell A2 comment: jx:image(src="logo" imageType="PNG" lastCell="A2")
logoBytes, _ := os.ReadFile("logo.png")
data := map[string]any{"logo": logoBytes}
xlfill.Fill("input/t10.xlsx", "output/10_image.xlsx", data)

Template cells: A1:"Merged Header"

Cell A1 comment:
jx:area(lastCell="C2")
jx:mergeCells(lastCell="C2" cols="3" rows="2")
data := map[string]any{}
xlfill.Fill("input/t11.xlsx", "output/11_mergecells.xlsx", data)

Template cells: A1:"Site" B1:"Link" A2:"${e.Name}" B2:"${hyperlink(e.URL, e.Name)}"

Cell A1 comment: jx:area(lastCell="B2")
Cell A2 comment: jx:each(items="sites" var="e" lastCell="B2")
data := map[string]any{
"sites": []any{
map[string]any{"Name": "Google", "URL": "https://google.com"},
map[string]any{"Name": "GitHub", "URL": "https://github.com"},
},
}
xlfill.Fill("input/t12.xlsx", "output/12_hyperlinks.xlsx", data)

Template cells: A1:"${dept.Name}" A2:"${e.Name}" B2:"${e.Role}"

Cell A1 comment:
jx:area(lastCell="B2")
jx:each(items="departments" var="dept" lastCell="B2")
Cell A2 comment:
jx:each(items="dept.Employees" var="e" lastCell="B2")
data := map[string]any{
"departments": []any{
map[string]any{
"Name": "Engineering",
"Employees": []any{
map[string]any{"Name": "Alice", "Role": "Lead"},
map[string]any{"Name": "Bob", "Role": "Dev"},
},
},
map[string]any{
"Name": "Sales",
"Employees": []any{
map[string]any{"Name": "Carol", "Role": "Manager"},
},
},
},
}
xlfill.Fill("input/t13.xlsx", "output/13_nested_each.xlsx", data)

Template cells: A1:"${dept.Name}" A2:"${dept.Head}"

Cell A1 comment:
jx:area(lastCell="A2")
jx:each(items="departments" var="dept" multisheet="sheetNames" lastCell="A2")
data := map[string]any{
"sheetNames": []any{"Engineering", "Sales", "HR"},
"departments": []any{
map[string]any{"Name": "Engineering", "Head": "Alice"},
map[string]any{"Name": "Sales", "Head": "Bob"},
map[string]any{"Name": "HR", "Head": "Carol"},
},
}
// Creates 3 sheets: Engineering, Sales, HR
xlfill.Fill("input/t14.xlsx", "output/14_multisheet.xlsx", data)

Template cells: A1:"Name" A2:"{{e.Name}}" (uses {{ }} instead of ${ })

Cell A1 comment: jx:area(lastCell="A2")
Cell A2 comment: jx:each(items="items" var="e" lastCell="A2")
data := map[string]any{
"items": []any{
map[string]any{"Name": "Alpha"},
map[string]any{"Name": "Beta"},
},
}
xlfill.Fill("input/t15.xlsx", "output/15_custom_notation.xlsx", data,
xlfill.WithExpressionNotation("{{", "}}"),
)
data := map[string]any{"items": []any{"X", "Y"}}
xlfill.Fill("input/t16.xlsx", "output/16_keep_template.xlsx", data,
xlfill.WithKeepTemplateSheet(true),
)

Template cells: A1:"${text}"

Cell A1 comment:
jx:area(lastCell="A1")
jx:autoRowHeight(lastCell="A1")
data := map[string]any{
"text": "This is a long text that should cause the row height to be adjusted automatically.",
}
xlfill.Fill("input/t17.xlsx", "output/17_autorowheight.xlsx", data)
data := map[string]any{"items": []any{"One", "Two", "Three"}}
outBytes, err := xlfill.FillBytes("input/t18.xlsx", data)
os.WriteFile("output/18_fill_bytes.xlsx", outBytes, 0o644)
tmplBytes, _ := os.ReadFile("input/t19.xlsx")
data := map[string]any{"items": []any{"Red", "Green", "Blue"}}
var out bytes.Buffer
xlfill.FillReader(bytes.NewReader(tmplBytes), &out, data)
os.WriteFile("output/19_fill_reader.xlsx", out.Bytes(), 0o644)