Excelize: writing XLSX files from Go without Excel installed
Go language library for reading and writing Microsoft Excel™ (XLAM / XLSM / XLSX / XLTM / XLTX) spreadsheets
At a glance
- What is it?
- Excelize is a pure Go library for reading and writing XLAM, XLSM, XLSX, XLTM and XLTX files, with a streaming API for large worksheets. It fits Go services that must emit or ingest spreadsheet files on a server, and it is the wrong tool when you need a formula engine or full recalculation.
- Who is it for?
- Adopt Excelize if you are writing a Go service that must produce or parse XLSX, XLSM, XLAM, XLTM or XLTX files on a machine where Excel is not installed, and if you can build on Go 1.25.0 or later. Do not adopt it as a calculation engine: the README documents reading and writing spreadsheets, not evaluating the formulas inside them, so anything that depends on recalculated results needs a different component.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem Excelize solves for Go services
A Go backend that has to hand a user a spreadsheet usually faces two bad options. Shell out to a headless office suite, which drags a large runtime and a process boundary into the deployment. Or write the Office Open XML parts by hand: the zip container, the workbook relationships, the shared string table, the styles part, the sheet XML. Excelize exists to remove the second option. The README describes it as a library written in pure Go providing functions that write to and read from XLAM, XLSM, XLSX, XLTM and XLTX files, supporting documents generated by Microsoft Excel 2007 and later. The audience is Go developers who need spreadsheet output as a file format, not as an interactive application. Typical shapes: an export endpoint that turns a query result into a downloadable workbook, a batch job that reads uploaded XLSX files and writes rows into a database, a reporting service that stamps a chart onto a template. The package name in the module path is github.com/xuri/excelize/v2, and the README states the library needs Go version 1.25.0 or later, which is a hard floor rather than a suggestion.
How a workbook is built and written
The object model is a workbook handle. excelize.NewFile() returns one, and every subsequent call hangs off it: NewSheet to add a worksheet, SetCellValue to write a single cell, SetSheetRow to write a row from a slice, SetActiveSheet to choose which sheet opens first, SaveAs to write to a path or Save to write back to the origin path. The README's chart example shows the row-writing path in practice: it loops over a slice of rows, converts a row index into a cell reference with excelize.CoordinatesToCellName(1, idx+1), and passes the address plus a pointer to the row into SetSheetRow. Reading runs the other way. excelize.OpenFile returns a handle, GetCellValue takes a worksheet name and a cell reference such as "Sheet1" and "B2", and GetRows returns the whole sheet as a slice of string slices. Two structural details matter more than the API surface. First, a workbook is a resource: every example in the README defers f.Close(), and the create example checks the error from Close rather than discarding it, which tells you the library holds state that has to be released. Second, the README states the library provides a streaming API for generating or reading data from a worksheet with huge amounts of data. That is the part of the design aimed at files too large to hold as an in-memory cell grid, and it is a separate code path from the SetCellValue and GetRows calls shown in the basic examples.
Charts and pictures are constructed as Go values
The chart API is the clearest illustration of how Excelize models spreadsheet objects. AddChart takes a worksheet name, an anchor cell such as "E1", and a pointer to an excelize.Chart struct. The struct carries a Type, here excelize.Col3DClustered, a slice of ChartSeries, and a Title. Each series names itself with a formula-style reference like "Sheet1!$A$2" and points Categories and Values at ranges such as "Sheet1!$B$1:$D$1" and "Sheet1!$B$2:$D$2". So the chart is defined by cell references, not by data the library holds separately, and the README notes you can build charts from worksheet data or generate charts with no worksheet data at all. The picture API is flatter: AddPicture takes a worksheet, a cell, a file path and an optional excelize.GraphicOptions pointer. The README's example passes ScaleX and ScaleY to resize, and in a second call sets PrintObject to a pointer to true, LockAspectRatio to false, OffsetX and OffsetY to 15 and 10, and Locked to a pointer to false. Note the pointer-to-bool pattern for the flags: the options struct distinguishes "unset" from "set to false", which is why the example declares enable and disable as local variables and takes their addresses. The example also imports image/gif, image/jpeg and image/png for their side effects, which means image decoding is registered through the standard library rather than bundled.
Getting it into a module and a first file
Installation is one command. Without Go modules, go get github.com/xuri/excelize. With modules, which the README recommends, go get github.com/xuri/excelize/v2. The import path in code is github.com/xuri/excelize/v2. A minimal program is short: call excelize.NewFile(), defer a Close that reports its error, call f.NewSheet("Sheet2") and check the returned error and index, write with f.SetCellValue("Sheet2", "A2", "Hello world.") and f.SetCellValue("Sheet1", "B2", 100), call f.SetActiveSheet(index), then f.SaveAs("Book1.xlsx"). Reading is the mirror image: excelize.OpenFile("Book1.xlsx"), defer Close, then f.GetCellValue("Sheet1", "B2") for one cell or f.GetRows("Sheet1") for the sheet. There is no configuration file and no environment variable in the material. The only version constraint is the Go toolchain: 1.25.0 or later, per the README. Everything else is function calls and struct literals.
What Excelize does not do
The README describes a library that reads and writes spreadsheet files. It does not describe a formula evaluation engine, and nothing in the supplied material suggests that GetCellValue computes a formula's result. If a workbook arrives with formulas whose cached values are stale or absent, the value you read is whatever the file contains. If your pipeline needs recalculated numbers, Excelize is the wrong component and you need something that evaluates the formula language, or a step that opens the file in a real spreadsheet application first. Two smaller boundaries are visible in the material. The format list is XLAM, XLSM, XLSX, XLTM and XLTX, so the older binary XLS format is outside the scope described. And the Go 1.25.0 floor is a real constraint for teams pinned to an older toolchain: this is not a library you can drop into a long-lived module on an old Go release without upgrading the toolchain first. The streaming API is the answer to memory pressure on large sheets, but the README only states that it exists; it does not enumerate its limits, so how it behaves on your file sizes is something you have to establish yourself.
Excelize versus a language-agnostic XLSX writer
The obvious alternative for many teams is a spreadsheet library in another language, for example a Python or Java writer invoked from the Go service, or a general OOXML toolkit that treats the file as XML parts rather than as a workbook object model. The difference in approach is where the abstraction sits. Excelize exposes workbook concepts directly: worksheets, cells, rows, charts, pictures, with Go structs for chart series and graphic options. A generic OOXML toolkit exposes the parts and the schema, and you assemble the XML yourself, which gives you access to anything the ECMA-376 specification allows but pushes the bookkeeping onto you. The README states that the XML is compliant with part 1 of the 5th edition of the ECMA-376 Standard, which is the specification both approaches ultimately target. Choosing Excelize means accepting its object model as the boundary of what you can express conveniently; choosing a raw OOXML route means accepting more code for the common cases in exchange for not being limited by a library's API surface. If your Go service already exists and the spreadsheet work is a small part of it, avoiding a second runtime and a process boundary is usually the deciding factor.
Maintenance, releases and the BSD-3-Clause licence
The repository is not archived, and the release cadence visible in the material is steady rather than rapid: v2.10.0 in October 2025, v2.10.1 in February 2026, v2.11.0 in July 2026, with the last push to the default branch in September 2026. The module path carries a v2 major version, so upgrades within v2 are the normal path and a v3 would be a deliberate migration. Because the library is pure Go with no cgo and no external office runtime, the upgrade cost is mostly the API changes between minor versions plus the Go toolchain floor, which the README currently sets at 1.25.0. That floor can rise between releases, and it is the item most likely to force work on a team that upgrades the library casually. The licence is BSD-3-Clause, a permissive licence that permits use in closed-source products provided the copyright notice and licence text are retained; the README links to opensource.org/licenses/BSD-3-Clause. That is a factual description of the licence identifier, not legal advice, and if you redistribute Excelize inside a product, the notice-retention clause is the part your legal review should look at.
Editorial conclusion
Adopt Excelize if you are writing a Go service that must produce or parse XLSX, XLSM, XLAM, XLTM or XLTX files on a machine where Excel is not installed, and if you can build on Go 1.25.0 or later. Do not adopt it as a calculation engine: the README documents reading and writing spreadsheets, not evaluating the formulas inside them, so anything that depends on recalculated results needs a different component. Before committing, verify three things against your own files: that the streaming API covers your read and write pattern, that the chart types and picture options you need appear in the package documentation, and that your licence review accepts BSD-3-Clause for the way you redistribute the binary.
Community notes