Library / SDK
jmcnamara/rust_xlsxwriter avatar
jmcnamara/rust_xlsxwriter

rust_xlsxwriter: writing Excel xlsx files from Rust, and only writing

A Rust library for creating Excel XLSX files.

595 stars56 forksRustApache-2.0

At a glance

What is it?
rust_xlsxwriter is a Rust crate for producing Excel 2007+ xlsx files, ported from the Python XlsxWriter by the same author, with charts, conditional formatting, rich text and a constant memory mode for very large files. It cannot read or modify an existing workbook, and that limit is deliberate.
Who is it for?
rust_xlsxwriter suits a Rust service that generates Excel reports, exports data for people who live in spreadsheets, or needs a memory bounded writer for very large workbooks, and it suits teams that also want the C, Python and Perl siblings to stay in step with the same fixes.
Can I use it commercially?
Yes. Apache-2.0 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 Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A writer with no read path

rust_xlsxwriter is a Rust crate for writing Excel files in the xlsx format. The README is explicit about what that means in practice: it can write text, numbers, dates and formulas to multiple worksheets in a new Excel 2007+ file, it has a focus on performance and fidelity with the format created by Excel, and it cannot be used to modify an existing file.

That last constraint appears twice, once in the introduction and once in the motivation list, where the design considerations include the entry 'Write only. The library only supports writing Excel files, and not reading or modifying them.' The project defends the choice by saying the Rust implementation seeks to avoid some of the required workarounds and API mistakes of the other language variants in the family, and that it deliberately has no FAQ section because the API is meant to be straightforward enough not to need one.

For most users that is a good trade, since the common case is generating a report from data. The case it does not cover is the round trip: reading a template, filling it in, and handing it back. A library that only writes cannot do that, and no amount of feature coverage changes it.

The crate is published on crates.io as `rust_xlsxwriter`, with the documentation hosted on docs.rs, and the project is a rewrite of the Python XlsxWriter library in Rust by the same author, with additional Rust-like features and APIs.

The API is a workbook, a worksheet and a format

The example in the README shows the whole shape of the library, and it is the familiar spreadsheet writer model rather than anything exotic. You create a `Workbook`, add a `Worksheet` to it, define reusable `Format` objects, and write values into cells by row and column index.

The setup is short:

rust
use rust_xlsxwriter::*;

fn main() -> Result<(), XlsxError> {
    // Create a new Excel file object.
    let mut workbook = Workbook::new();

    // Create some formats to use in the worksheet.
    let bold_format = Format::new().set_bold();
    let decimal_format = Format::new().set_num_format("0.000");
    let date_format = Format::new().set_num_format("yyyy-mm-dd");
    let merge_format = Format::new()
        .set_border(FormatBorder::Thin)
        .set_align(FormatAlign::Center);

Everything returns a `Result` with an `XlsxError`, which is the Rust way of saying that a write can fail and the caller has to decide what to do about it. The formats are values with builder-style setters, and the same `Format` is then reused across many cells rather than being rebuilt per cell.

Writing itself is by index:

rust
    // Add a worksheet to the workbook.
    let worksheet = workbook.add_worksheet();

    // Set the column width for clarity.
    worksheet.set_column_width(0, 22)?;

    // Write a string without formatting.
    worksheet.write(0, 0, "Hello")?;

    // Write a string with the bold format defined above.
    worksheet.write_with_format(1, 0, "World", &bold_format)?;

    // Write some numbers.
    worksheet.write(2, 0, 1)?;

The typed variants matter more than they look. A formula is written with `Formula::new("=SIN(PI()/4)")`, a date with `ExcelDateTime::from_ymd(2023, 1, 25)`, and a hyperlink with `Url::new`, optionally with display text attached. A date is a value with its own type rather than a preformatted string, which is what lets the same number format apply to it, and a link is a distinct object rather than a magic string.

rust
    // Write a formula.
    worksheet.write(5, 0, Formula::new("=SIN(PI()/4)"))?;

    // Write a date.
    let date = ExcelDateTime::from_ymd(2023, 1, 25)?;
    worksheet.write_with_format(6, 0, &date, &date_format)?;

    // Write some links.
    worksheet.write(7, 0, Url::new("https://www.rust-lang.org"))?;
    worksheet.write(8, 0, Url::new("https://www.rust-lang.org").set_text("Rust"))?;

    // Write some merged cells.
    worksheet.merge_range(9, 0, 9, 1, "Merged cells", &merge_format)?;

And the workbook is closed by saving it:

rust
    // Insert an image.
    let image = Image::new("examples/rust_logo.png")?;
    worksheet.insert_image(1, 2, &image)?;

    // Save the file to disk.
    workbook.save("demo.xlsx")?;

    Ok(())
}

Feature flags decide what the crate pulls in

The dependency situation is unusually tidy. The default feature set is described as including all the standard functionality, and the only dependency in that case is the `zip` crate, because an xlsx file is a zip archive of XML parts. Everything else is opt in.

Adding the crate is a normal cargo command:

bash
cargo add rust_xlsxwriter

and a feature is enabled the same way:

bash
cargo add rust_xlsxwriter -F constant_memory

The optional features are worth reading as a list of integrations rather than as a list of switches. `constant_memory` keeps memory usage to a minimum when writing large files. `serde` adds support for Serde serialization. `chrono` and `jiff` add support for those date and time types to the API, alongside the native `ExcelDateTime`. `polars` adds the mapping between `PolarsError` and `rust_xlsxwriter::XlsxError`, which pairs with the separate `polars_excel_writer` crate. `rust_decimal` adds support for writing the `rust_decimal` `Decimal`.

Two of them are about deployment rather than data. The `wasm` feature adds a dependency on `js-sys` and `wasm-bindgen` to allow compilation for wasm and JavaScript targets, which is a browser spreadsheet writer rather than a server one. And `zmij` is described as a dependency that speeds up writing numeric worksheet cells by around 10%.

The `enhanced_autofit` feature is the odd one out. It adds support for autofitting formatted numbers and dates via the `ssfmt` crate, which exists because the number of characters a formatted value occupies is not the same as the number of characters in the underlying number. Anyone generating a report where a column holds thousands with a currency format and expects the column to be wide enough has a reason to turn it on.

The manifest makes the same structure explicit: the package requires Rust 1.88.0, with a comment that this is for zip.rs compatibility, and the optional dependencies are listed individually so you can see what each flag costs.

Constant memory mode and what large files really cost

The crate's answer to writing a very large workbook is the `constant_memory` feature, and the README's release notes list the addition of support for writing directly to STDOUT alongside general performance improvements. Both of those are aimed at the same problem: a report that does not fit in memory or cannot be staged on disk.

The ordinary mode builds the workbook in memory and writes the file at the end, which is why `workbook.save("demo.xlsx")` is the last step and why the API is convenient: nothing has to be flushed until the end. That convenience has a cost, and the cost scales with the size of the workbook. A writer that keeps everything in memory until the save is the wrong shape for a table with millions of rows.

Constant memory mode is the alternative shape, and the important part of understanding it is that it changes what the API can do. In a streaming writer, rows have to be written in order, because the writer cannot go back and insert a row it has already passed. That is a genuine constraint on a spreadsheet library whose normal interface is random access by row and column index, and it is the trade for the memory bound.

The wider feature list is what makes the library competitive rather than merely adequate. The supported features include all basic Excel data types, full cell formatting, formulas including new Excel 365 dynamic functions, charts, hyperlink support, page and printing setup, merged ranges, conditional formatting, data validation, cell notes, textboxes, checkboxes, sparklines, worksheet images in PNG, JPEG, GIF and BMP, workbook themes, rich multi-format strings, outline groupings, defined names, autofilters, worksheet tables, Serde serialization and macro support.

Charts and sparklines in a Rust crate are unusual. The examples directory shows the shape of that support, with separate files for area, bar, clustered, column, combined, doughnut, gauge, gradient, line, pareto, pattern, pie, radar, scatter, secondary axis and stock charts.

Performance claims and a family of libraries

The README states a relative performance comparison between the C, Rust and Python versions of the library: `rust_xlsxwriter` is 1.6x faster than C/libxlsxwriter, and 6.5x faster than Python/XlsxWriter. Those are the project's own figures, published with a link to the Performance section of the documentation, and the design considerations separately claim the library is within 10% of the similar C library. The `zmij` dependency is separately credited with speeding up writing numeric worksheet cells by around 10%.

The family point is the more interesting design decision. The README lists sister libraries written in C, in Python and in Perl, by the same author, and states that bug fixes and improvements in one get transferred to the others. There is also a Perl distribution, Excel::Writer::XLSX, and the Rust crate's topics include `libxlsxwriter` and `xlsxwriter`.

The consequence is that a team can move between languages without changing the shape of the code, which matters for organisations that have a Python reporting service and a Rust data pipeline producing similar workbooks. The cost is that the four implementations share a design, and a feature that is awkward in one language is awkward in all of them, which is what the 'No FAQ section' note in the motivation list is really about.

For a Rust user specifically, the case for choosing the Rust implementation over the C one is the API and the safety, and the case for choosing it over the Python one is the deployment story. The performance figures are the project's own and should be treated as such.

Licensing, versions and the shape of the repository

The licence is dual, and the manifest is explicit about it: `MIT OR Apache-2.0`, with `LICENSE_Apache2.0` and `LICENSE_MIT` both in the repository root. That is the permissive pair, so you can use the crate in a closed source product under either of the two terms, and the choice is yours. This is a description of what the licence text grants rather than advice on your obligations.

The package metadata records the version as 0.100.0, with the author as John McNamara, the edition as 2021 and the minimum Rust version as 1.88.0 with a comment noting it is for zip.rs compatibility. The repository publishes no GitHub releases, and the changelog lives in `CHANGELOG.md` in the repository, with recent entries listed in the README under release notes.

The layout is that of a mature library. `src/` holds the implementation, `tests/` the test suite, `benches/` the Criterion benchmarks with two named benchmarks for float formatting and write performance, `examples/` the runnable examples, and `macros/` the derive macros used by the Serde integration. The dev dependencies are `regex`, `pretty_assertions` and `criterion`, which is a small and sensible set.

The project also carries a `.typos.toml` configuration, which is a sign of a maintainer who cares about the details of the repository, and a `CHANGELOG.md` maintained alongside the code.

On upkeep, the repository is not archived and the last push was on 2026-10-20, with the release notes showing that the recent direction of work is performance and streaming output. For a library at version 0.100.0 that is a maturity signal of its own, since a hundred minor releases of a 0.x crate means the author has been shipping small changes continuously rather than waiting for a large redesign.

Editorial conclusion

rust_xlsxwriter suits a Rust service that generates Excel reports, exports data for people who live in spreadsheets, or needs a memory bounded writer for very large workbooks, and it suits teams that also want the C, Python and Perl siblings to stay in step with the same fixes. It does not suit anyone who needs to open, edit or read an existing xlsx, because the crate is write only, and it does not suit a project that wants a single dependency with no flags, since the date and time, Serde, Polars and WebAssembly integrations are all optional features you have to turn on. Before adopting, check the format coverage your templates need, the file size where constant memory mode becomes necessary, and that the licence choice, MIT or Apache-2.0, matches your distribution requirements.

Frequently asked questions

Can rust_xlsxwriter read or modify an existing Excel file?

No. The library is write only, and that is a stated design decision rather than a missing feature. It writes new Excel 2007+ xlsx files and cannot be used to modify an existing one.

How do I write a large Excel file without running out of memory?

Enable the `constant_memory` feature with `cargo add rust_xlsxwriter -F constant_memory`. It keeps memory usage to a minimum when writing large files, and the release notes also mention support for writing directly to STDOUT.

Which date and time types can rust_xlsxwriter write?

The native `ExcelDateTime` type is always available. The optional `chrono` feature adds support for Chrono types and the `jiff` feature adds Jiff types, both through the `IntoExcelDateTime` trait.

What licence does rust_xlsxwriter use?

The manifest records `MIT OR Apache-2.0`, and the repository ships both `LICENSE_Apache2.0` and `LICENSE_MIT`, so you can pick either permissive option.

How does rust_xlsxwriter compare with the Python XlsxWriter?

It is a rewrite of the Python XlsxWriter in Rust by the same author, and the project states a relative comparison of 1.6x faster than C/libxlsxwriter and 6.5x faster than Python/XlsxWriter. It also notes that bug fixes and improvements are transferred between the C, Python, Perl and Rust versions.

Official sources

  1. Issues
  2. jmcnamara/rust_xlsxwriter on GitHub
  3. License: Apache-2.0
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/jmcnamara-rust-xlsxwriter.svg)](https://hysenlabs.com/projects/jmcnamara-rust-xlsxwriter)

Was this useful?