# ClosedXML: writing .xlsx files from .NET without Excel installed

> ClosedXML wraps the OpenXML SDK in a cell-and-worksheet API so .NET code can build Excel reports on a server. The README is explicit that the public API is still unstable, and that shapes how you should adopt it.

**ClosedXML/ClosedXML** — ClosedXML is a .NET library for reading, manipulating and writing Excel 2007+ (.xlsx, .xlsm) files. It aims to provide an intuitive and user-friendly interface to dealing with the underlying OpenXML API.

- Repository: https://github.com/ClosedXML/ClosedXML
- Stars: 5,721 · Forks: 937
- Language: C#
- License: MIT
- Published: 2026-09-22 · Updated: 2026-09-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/closedxml-closedxml

## The problem ClosedXML solves for .NET services

Producing an .xlsx file usually means either driving Excel through COM automation or writing the OpenXML markup by hand. The first requires Excel on the machine and does not belong in a server process. The second means working with the ECMA-376 package structure, which the README calls "a large and complicated beast." ClosedXML exists to sit between those two options: it is a .NET library for reading, manipulating and writing Excel 2007+ (.xlsx, .xlsm) files, aiming at an intuitive interface over the OpenXML API. The README's stated typical case is creating Excel reports on a web server, which tells you the intended audience: backend developers who need a downloadable spreadsheet and have no Excel installation to lean on. It is not a spreadsheet engine for calculation-heavy workbooks, and it is not a UI.

## How the workbook object model maps onto OpenXML

The API is organised the way a spreadsheet is: an XLWorkbook holds worksheets, a worksheet holds cells, and a cell holds a value or a formula. The README's example creates an XLWorkbook, adds a sheet named "Sample Sheet", writes a string into A1, sets A2 with the FormulaA1 property to "=MID(A1, 7, 5)", then calls SaveAs. The formula is stored as a formula string, not evaluated by ClosedXML, so the computed value appears when Excel or another consumer opens the file. Saving writes the OpenXML package. Loading reverses this: the library reads the package back into the same object model, which is why the README's load benchmarks are separate from its save benchmarks. Because the object model is a wrapper, anything the underlying OpenXML representation cannot express is also out of reach here, and the README asks users to implement missing features themselves and open a pull request rather than only filing an issue.

## Installing ClosedXML and writing a first file

The README gives one installation route: NuGet, either through the package page or the Package Manager Console. The command below is copied from the README; running it in Visual Studio's Package Manager Console pulls the package into the selected project.

```bash
PM> Install-Package ClosedXML
```

For SDK-style projects the equivalent is adding a PackageReference, and the package name is ClosedXML. Once referenced, the README's own minimal example is the shortest path to a real file. It writes one label and one formula, then saves to disk.

```c#
using (var workbook = new XLWorkbook())
{
    var worksheet = workbook.Worksheets.Add("Sample Sheet");
    worksheet.Cell("A1").Value = "Hello World!";
    worksheet.Cell("A2").FormulaA1 = "=MID(A1, 7, 5)";
    workbook.SaveAs("HelloWorld.xlsx");
}
```

After that runs, HelloWorld.xlsx exists in the working directory, A1 reads Hello World! and A2 holds the formula. Two platform notes from the README matter before you get that far. On Linux, an exception reading "The type initializer for 'Gdip' threw an exception." means you need 0.97 or later. And if a font cannot be resolved, the README documents setting LoadOptions.DefaultGraphicEngine to a new DefaultGraphicEngine with a fallback font name.

## Where ClosedXML is the wrong tool

Thread safety is the sharpest constraint in the README. ClosedXML is not thread-safe, the README states there is no guarantee that parallel operations will work, and it adds that the underlying OpenXML library is not thread-safe either. If your design assumes several threads writing sheets into one workbook, that design is outside what the project promises. Memory is the second constraint. The README's own figures for a text-only workbook of 1,000,000 rows by 10 columns show 6.302 seconds and 402 MiB just to insert data, then 17.134 seconds to save, with total memory of 1880 MiB. Those numbers come from the project's published benchmark table, not from an independent run, but they are the project's own accounting, and they describe a library that holds workbook state in memory. Loading the same text-only file took 49.046 seconds and 801 MiB. If your job is streaming millions of rows into a file on a small container, this is not the right shape of tool. The README also does not document rollback behaviour for a failed SaveAs, so do not assume a partial file is cleaned up.

## ClosedXML vs the OpenXML SDK and EPPlus

The comparison the README itself invites is with the OpenXML SDK. ClosedXML is a wrapper around it, so the difference is level of abstraction rather than capability: with the raw SDK you construct and traverse package parts, and with ClosedXML you call Worksheets.Add and set Cell("A1").Value. Choosing the SDK means more code and closer control over what lands in the package; choosing ClosedXML means less code and accepting the wrapper's coverage and its API churn. The other comparison people reach for is EPPlus, and the meaningful difference here is licensing: ClosedXML is MIT, while EPPlus changed its licence model in its 5.x line. If your constraint is a permissive licence with no commercial tier to negotiate, that is a reason to look at ClosedXML first. The README does not compare features against EPPlus, so treat any feature-by-feature claim as something you verify against both projects' documentation rather than something this repository asserts.

## Upgrade cost and what the MIT licence means here

The README is unusually direct: "The public API is still not stable and it is a very good idea to read release notes and migration guide before each update." It links a migration guide for 0.100 and release notes for 0.100 and 0.97. That is the real maintenance cost of this dependency. The version history reinforces it: 0.105.0 arrived in May 2025, 0.105.1 in July 2026, and the repository's last push was on 2026-09-18, so the project is moving. A version bump is not a drop-in event; budget for reading the migration guide, and pin the package version so an upgrade is a deliberate change rather than something that arrives with a restore. On licensing, the repository is MIT, which permits commercial use and modification, but the README's note that features depend on community pull requests means the practical question is not permission but who fixes the gap you hit. This is a description of the licence text, not legal advice; read LICENSE and your own counsel if the distinction matters to you.

## Conclusion

Adopt ClosedXML when a .NET service must produce .xlsx output and no Excel process is available, and pin the package version because the README states the public API is not stable and points readers at release notes and a migration guide before each update. Do not adopt it for concurrent writes from multiple threads, since the README states ClosedXML is not thread-safe and gives no guarantee that parallel operations work, and the underlying OpenXML library is not thread-safe either. Verify first that your target framework satisfies .NET Framework >= 4.0 or .NET Standard >= 2.0, and if you run on Linux, check that you are on 0.97 or later before shipping anything.

## FAQ

### Is ClosedXML free or paid?

ClosedXML is MIT licensed, so there is no paid tier and no licence fee to use it. The package is distributed through NuGet.

### What is ClosedXML and what is it used for?

It is a .NET library for reading, manipulating and writing Excel 2007+ (.xlsx, .xlsm) files, built as a friendlier interface over the OpenXML API. The README's typical example is creating Excel reports on a web server.

### What is the difference between OpenXML and ClosedXML?

ClosedXML is a wrapper around the OpenXML SDK rather than a replacement for it. With the SDK you work with the package parts directly; with ClosedXML you work with workbooks, worksheets and cells.

### Does ClosedXML need Excel installed?

No. The README states that ClosedXML allows you to create Excel files without the Excel application, which is why it fits server-side report generation.

### How do I install ClosedXML?

The README gives the Package Manager Console command Install-Package ClosedXML, or you can install it directly from NuGet. The README also links a migration guide and release notes that it recommends reading before each update.

## Sources

- [ClosedXML/ClosedXML on GitHub](https://github.com/ClosedXML/ClosedXML)
- [Issues](https://github.com/ClosedXML/ClosedXML/issues)
- [License: MIT](https://github.com/ClosedXML/ClosedXML/blob/develop/LICENSE)
- [README](https://github.com/ClosedXML/ClosedXML/blob/develop/README.md)
- [Releases](https://github.com/ClosedXML/ClosedXML/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/closedxml-closedxml
