# \[ANN\] XLSX.jl v0.11.0 now released, bringing significant new functionality

**URL:** <https://discourse.julialang.org/t/ann-xlsx-jl-v0-11-0-now-released-bringing-significant-new-functionality/136345>\
**Category:** Package Announcements\
**Tags:** package, xlsx\
**Created:** [March 23, 2026, 3:25pm UTC](https://discourse.julialang.org/t/ann-xlsx-jl-v0-11-0-now-released-bringing-significant-new-functionality/136345 "2026-03-23T15:25:29Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![TimG](https://avatars.discourse-cdn.com/v4/letter/t/82dd89/32.png) [@TimG](https://discourse.julialang.org/u/TimG)\
**Post date:** [March 23, 2026, 3:25pm UTC](https://discourse.julialang.org/t/ann-xlsx-jl-v0-11-0-now-released-bringing-significant-new-functionality/136345/1 "2026-03-23T15:25:29Z")

</div>

This release introduces significant new functionality as set out below.

There are almost no changes in existing functional APIs in v0.11.0 compared with v0.10.4. Those changes that have been made are described briefly here.

This version drops support for Julia v1.6, and requires at least Julia v1.8.

## Breaking changes

There is only one breaking change in this version:

- `infer_eltypes` now defaults to `true` (e.g. in `gettable` and `readtable`). This is the more common use case but, if it is not _your_ use case, you will need explicitly to set `infer_eltypes = false` in the relevant functions.

All other changes either introduce new functionality (documented elsewhere) or relate to internals only.

## New Functions

A number of new functions have been added compared with v0.10.4.

These include 18 new functions to support formatting of cells and cell values together with functions to copy or delete a sheet, to merge cells and to add new defined names for cells or cell ranges. In addition, it is now also possible to assign `AnnotatedStrings` (from [StyledStrings.jl](https://github.com/JuliaLang/StyledStrings.jl)) to cells to create content using Excel’s rich text formatting.

A new function, `XLSXFile`, is provided that takes a `Tables.jl` compatible table and creates a new `XLSXFile` object for writing and which can act as a sink for functions such as `CSV.read`.

A new function, `renamesheet!` is created to replace `rename!` for consistency in naming with `addsheet!`, `copysheet!` and `deletesheet!` and to avoid potential name conflicts when exported (e.g. with `DataFrames.rename!`). However, the existing function `XLSX.rename!` is retained (but not exported) to avoid a breaking change.

Two new functions, `gettransposedtable` and `readtransposedtable`, mirror `gettable` and `readtable` for worksheet tables that have data organised in rows rather than columns.

Some additional convenience functions have also been added to streamline functions that were already available (such as `newxlsx`, `savexlsx`).

A wide range of additional indexing options is now widely supported by most functions. Most functions now support indexing rows and columns using vectors, ranges and step ranges and will accept a colon.

## Exported Functions

Most useful functions are now public, and can be used without the `XLSX.` prefix. The following function names are now exported:

- Files and worksheets  
`XLSXFile`, `readxlsx`, `openxlsx`, `opentemplate`, `newxlsx`, `writexlsx`, `savexlsx`,  
`Worksheet`, `sheetnames`, `sheetcount`, `hassheet`,  
`addsheet!`, `renamesheet!`, `copysheet!`, `deletesheet!`

- Cells & data  
`CellRef`, `row_number`, `column_number`, `eachtablerow`,  
`readdata`, `getdata`, `gettable`, `readtable`, `readto`,  
`gettransposedtable`, `readtransposedtable`,  
`writetable`, `writetable!`,  
`setFormula`,  
`addDefinedName`

- Formats  
`setFormat`, `setFont`, `setBorder`, `setFill`, `setAlignment`,  
`setUniformFormat`, `setUniformFont`, `setUniformBorder`, `setUniformFill`, `setUniformAlignment`, `setUniformStyle`,  
`setConditionalFormat`,  
`RichTextString`, `RichTextRun`,  
`setColumnWidth`, `setRowHeight`,  
`getMergedCells`, `isMergedCell`, `getMergedBaseCell`, `mergeCells`

The iterator `XLSX.eachrow` has retained the XLSX prefix to avoid making a breaking change. However, `Base.eachrow` now refers to `XLSX.eachrow` for `XLSX.Worksheet` data types, meaning that `eachrow` can be used without qualification, too.

## Fixed issues

This release addresses the following issues:

> **Full list**
>
> - [https://github.com/JuliaData/XLSX.jl/issues/52](https://github.com/JuliaData/XLSX.jl/issues/52)
> - [Writing Excel files is very slow · Issue #61 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/61)
> - [Feature request: creating & applying cell styles and conditional formatting · Issue #63 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/63)
> - [Add a way to delete an existing sheet from an Excel file · Issue #80 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/80)
> - [indexing Rows with vectors · Issue #88 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/88)
> - [examples of using cell.formula · Issue #120 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/120)
> - [Iteration interface enhancement for the row iterator · Issue #147 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/147)
> - [Defining named ranges · Issue #148 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/148)
> - [How to get a single row just by a row number? · Issue #150 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/150)
> - [Defaults / interface for XLSX.readtable · Issue #155 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/155)
> - [writetable with multiple sheets generates .xlsx file with only one sheet visible to applications with less robust .xlsx readers · Issue #156 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/156)
> - [Some formulas get overwritten with values when editing existing workbook · Issue #159 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/159)
> - [Extracting hyperlinks from cells? · Issue #165 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/165)
> - [Unexpected Content Type · Issue #172 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/172)
> - [Putting NaN in a cell causes Excel to see an error, but it can recover it. · Issue #179 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/179)
> - [1 Combine/merge cells. 2 formating table boundaries · Issue #184 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/184)
> - [Named range being parsed with erroneous single quotes · Issue #189 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/189)
> - [Asserts should not be used for validation · Issue #190 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/190)
> - [Recommended way to clear an area in an existing Excel sheet · Issue #198 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/198)
> - [Sheet is incorrectly shown as 1x1 · Issue #222 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/222)
> - [c.formula, get full formula text for external links · Issue #224 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/224)
> - [Simplified way to set cell formulas · Issue #232 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/232)
> - [Applying number format to columns · Issue #234 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/234)
> - [Shared formulas not supported · Issue #235 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/235)
> - [[Bug] Error showing value of type XLSX.XLSXFile, which contains unicode column name · Issue #238 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/238)
> - [Can supported types (for writing to Excel) be expanded - similar to CSV.write ? · Issue #239 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/239)
> - [Handling of the merged cells · Issue #241 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/241)
> - [Cannot open file (failed to parse internal XML file) · Issue #243 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/243)
> - [Password protected xlsx files · Issue #251 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/251)
> - [Manually invoking the GC leads to very bad performance if you read many files · Issue #252 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/252)
> - [Failed to parse internal XML file `xl/comments95.xml` · Issue #253 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/253)
> - [Cell References unexpected behaviour · Issue #258 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/258)
> - [XLSX cell format · Issue #259 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/259)
> - [Offer `normalizenames`? · Issue #260 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/260)
> - [addsheet! does not append "/xl/worksheets/sheet\*.xml" info to [Content\_Types].xml · Issue #275 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/275)
> - [getindex with vector of integer indexes · Issue #276 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/276)
> - [Add PrecompileTools · Issue #277 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/277)
> - [gettable does not work? · Issue #278 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/278)
> - [Writing to a cell in a template overwrites existing formats · Issue #281 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/281)
> - [Illegal characters can corrupt exported xlsx file · Issue #284 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/284)
> - [XML.simple\_value is only defined for simple nodes · Issue #299 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/299)
> - [Corruption of \< and \> in formula · Issue #301 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/301)
> - [Broken link to ECMA Standard 376 on readme and docs · Issue #305 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/305)
> - [Cannot create a cell value with leading whitespace · Issue #308 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/308)
> - [Lazy Parsing Feature? · Issue #311 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/311)
> - [Date vs DateTime when the time is 0:00:00 · Issue #314 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/314)
> - [possible release notes for v0.11.0 · Issue #316 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/316)
> - [Cannot round-trip dynamic array functions · Issue #324 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/324)
> - [== for SheetCellRef is broken · Issue #331 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/331)
> - [Request: Make a new release · Issue #335 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/335)
> - [https://github.com/JuliaData/XLSX.jl/issues/338](https://github.com/JuliaData/XLSX.jl/issues/338)
> - [https://github.com/JuliaData/XLSX.jl/issues/342](https://github.com/JuliaData/XLSX.jl/issues/342)

[~~Where have the Issue titles gone? They show in preview.~~ They’re back!]

# Documentation

The documentation for this package has been extended substantially to cover the new functionality and all changes are (should be) reflected therein. In particular, a detailed guide to using the new formatting functions has been added.

## Internal changes

A number of changes to package internals have been made. Specifically, changes have been made to the following data `struct`s:

- `SheetRowStreamIteratorState`
- `WorksheetCacheIteratorState`
- `WorksheetCache`
- `XLSXFile`
- `Workbook`
- `Worksheet`
- `SheetRow`
- `Cell`

In particular, the internal memory configuration of an `XLSXFile` object and its components has been changed significantly, nearly halving the package’s memory footprint.

## Changed dependencies

v0.11.0 has now fully migrated to `ZipArchives.jl` whereas v0.10.4 relied upon both this and `ZipFiles.jl`. In addition, xml support is now from `XML.jl` rather than `EzXML.jl`.

The use of `AnnotatedStrings` is supported through a package extension. This requires `StyledStrings.jl` to be in the active environment.

New functionality that has been added has brought the following additional dependencies compared with v0.10.4:

- `Colors.jl`
- `UUIDs.jl`
- `Random.jl`

In addition, the test suite now has dependencies on `CSV.jl`, `Distributions.jl` and `StyledStrings.jl`.

## Precompilation

v0.11.0 now makes use of `PrecompileTools.jl` (initially only in a small way).

---

<div class="post-metadata">

**Author:** ![Alexander\_Knudson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alexander_knudson/32/215656_2.png) [@Alexander\_Knudson](https://discourse.julialang.org/u/Alexander_Knudson)\
**Post date:** [April 1, 2026, 10:12pm UTC](https://discourse.julialang.org/t/ann-xlsx-jl-v0-11-0-now-released-bringing-significant-new-functionality/136345/2 "2026-04-01T22:12:38Z")

</div>

Fantastic work, Tim! XLSX.jl has been my favorite interface to Excel files, and I’m happy to see my feature request make it into the package. Do you have a roadmap for future releases?

I haven’t looked into it, but could plotting support be added? (Edit) I see that this has been requested in the past: [Add plotted figures to existing excel file · Issue #134 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/134). I’ll see if I can do some groundwork and will take any hints that you might have 🙂

---

<div class="post-metadata">

**Author:** ![TimG](https://avatars.discourse-cdn.com/v4/letter/t/82dd89/32.png) [@TimG](https://discourse.julialang.org/u/TimG)\
**Post date:** [April 2, 2026, 8:15am UTC](https://discourse.julialang.org/t/ann-xlsx-jl-v0-11-0-now-released-bringing-significant-new-functionality/136345/3 "2026-04-02T08:15:31Z")

</div>

In terms of a roadmap, I’m not currently looking very far ahead. My next task will be to address a significant [proposed change in XML.jl](https://github.com/JuliaComputing/XML.jl/pull/54). This is going to take a bit of work, I think. I’ve done some preliminary work but need to resume again now v0.11 is released.

(I added a comment on [issue #134](https://github.com/JuliaData/XLSX.jl/issues/134))

---

<div class="post-metadata">

**Author:** ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)\
**Post date:** [April 3, 2026, 12:31am UTC](https://discourse.julialang.org/t/ann-xlsx-jl-v0-11-0-now-released-bringing-significant-new-functionality/136345/4 "2026-04-03T00:31:34Z")

</div>

I am happy to see this happening @TimG ! 🎉

Thank you very much for reviving and maintaining this important package.

Cheers!

---

<div class="post-metadata">

**Author:** ![Jake](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jake/32/46007_2.png) [@Jake](https://discourse.julialang.org/u/Jake)\
**Post date:** [April 3, 2026, 10:51pm UTC](https://discourse.julialang.org/t/ann-xlsx-jl-v0-11-0-now-released-bringing-significant-new-functionality/136345/5 "2026-04-03T22:51:38Z")

</div>

I also am a happy user of XLSX.jl. For another project I use OdsIO.jl, which calls a Python program. It works well, but it would be neat to see its functionality in pure Julia. I was curious if that would be possible in this program, or if the file types are just too different.

---

<div class="post-metadata">

**Author:** ![TimG](https://avatars.discourse-cdn.com/v4/letter/t/82dd89/32.png) [@TimG](https://discourse.julialang.org/u/TimG)\
**Post date:** [April 9, 2026, 5:28pm UTC](https://discourse.julialang.org/t/ann-xlsx-jl-v0-11-0-now-released-bringing-significant-new-functionality/136345/6 "2026-04-09T17:28:27Z")

</div>

Glad you’re happy!

My feeling is that it is probably best to keep XLSX.jl just focused on Excel files. There is plenty of opportunity for further development in that space without widening scope to include .ods files, too. I’ve no experience with .ods files, so I don’t have any idea how similar/different they are from Excel files.

---

<div class="post-metadata">

**Author:** ![TimG](https://avatars.discourse-cdn.com/v4/letter/t/82dd89/32.png) [@TimG](https://discourse.julialang.org/u/TimG)\
**Post date:** [May 18, 2026, 10:02am UTC](https://discourse.julialang.org/t/ann-xlsx-jl-v0-11-0-now-released-bringing-significant-new-functionality/136345/7 "2026-05-18T10:02:33Z")

</div>

> [@Alexander\_Knudson](#):
>
> I haven’t looked into it, but could plotting support be added? (Edit) I see that this has been requested in the past: [Add plotted figures to existing excel file · Issue #134 · JuliaData/XLSX.jl · GitHub](https://github.com/JuliaData/XLSX.jl/issues/134). I’ll see if I can do some groundwork and will take any hints that you might have 🙂

I have closed [#134](https://github.com/JuliaData/XLSX.jl/issues/134) now with a fix (in v0.11.8) that allows any png, jpg or gif image to be added to a worksheet. See the [example in the docs](https://juliadata.org/XLSX.jl/dev/examples/#Adding-a-plot-image).

---

<div class="post-metadata">

**Author:** ![TimG](https://avatars.discourse-cdn.com/v4/letter/t/82dd89/32.png) [@TimG](https://discourse.julialang.org/u/TimG)\
**Post date:** [July 31, 2026, 11:32am UTC](https://discourse.julialang.org/t/ann-xlsx-jl-v0-11-0-now-released-bringing-significant-new-functionality/136345/8 "2026-07-31T11:32:52Z")

</div>

Here is a small update - XLSX is now on v0.12.2.

Two big changes - one internal and one in terms of functionality.

Internally, XLSX.jl now uses XML.jl v0.4. The version bump in XML was significant and required considerable work in XLSX. The main consequence has been the ability to remove much of the brittle multi-threading I had introduced to try to limit speed regressions vs v0.10 (which used EzXML.jl). XML.jl v0.4 has introduced much faster parsing mechanisms which have worked really well for XLSX.jl, restoring speed broadly to parity with the old EzXML.jl based version while radically simplifying the code and reducing memory use. Huge thanks to @joshday and @mathieu17g for this.

Functionally, I have added support for Excel-native tables. You are now able to create new Excel-native tables using XLSX.jl, add and manipulate summary rows (column sum, count, average, etc), read Excel-native tables directly by name to a DF or other Tables.jl sink, export DataFrames or other Tables.jl sources directly as Excel-native tables, etc.

Using Excel-native tables is much easier than the “old” way of reading and writing tablular data because Excel-native tables already describe themselves - particularly their extent - so it is no longer necessary to infer this from cell data. The “old” way still exists, so nothing is broken. But working with Excel-native tables is ([according to Microsoft](https://support.microsoft.com/en-US/Excel/overview-of-excel-tables)) a better way to work.

In addition, I’ve restored the ability of FileIO.jl to `load` and `save` Excel files. This broke several years ago when the previous package FileIO.jl relied upon stopped working but now works again through XLSX.jl.

Also, and with considerable, very-much-appreciated support from @Ronis_BR, Excel is now available as a backend for PrettyTables.jl.

Feedback, issues, PRs welcome, as always!

---

<div class="post-metadata">

**Author:** ![Jake](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jake/32/46007_2.png) [@Jake](https://discourse.julialang.org/u/Jake)\
**Post date:** [September 25, 2026, 1:03pm UTC](https://discourse.julialang.org/t/ann-xlsx-jl-v0-11-0-now-released-bringing-significant-new-functionality/136345/9 "2026-09-25T13:03:12Z")

</div>

In Excel, if I right click on a cell and then `Define Name` I give the cell or a cell range a name. I think this is different from the tables that you mention above. Is there a way to retrieve a cell (or range) by the cell name?

---

<div class="post-metadata">

**Author:** ![TimG](https://avatars.discourse-cdn.com/v4/letter/t/82dd89/32.png) [@TimG](https://discourse.julialang.org/u/TimG)\
**Post date:** [September 25, 2026, 1:40pm UTC](https://discourse.julialang.org/t/ann-xlsx-jl-v0-11-0-now-released-bringing-significant-new-functionality/136345/10 "2026-09-25T13:40:04Z")

</div>

Yes, you can just use the name in place of the cell reference. If cell B4 is named as “named\_cell” then `sheet["B4"]` and `sheet["named_cell"]` should both work. This is covered in the [XLSX Tutorial](https://juliadata.org/XLSX.jl/stable/tutorial/XLSXtutorial/#XLSX-Tutorial).

---

<div class="post-metadata">

**Author:** ![TimG](https://avatars.discourse-cdn.com/v4/letter/t/82dd89/32.png) [@TimG](https://discourse.julialang.org/u/TimG)\
**Post date:** [October 2, 2026, 9:52pm UTC](https://discourse.julialang.org/t/ann-xlsx-jl-v0-11-0-now-released-bringing-significant-new-functionality/136345/11 "2026-10-02T21:52:54Z")

</div>

I’ve just tagged another new release of XLSX.jl - v0.13.0. The main addition is support for native Excel charts - reading, editing, creating. The new functionality offers extensive but not comprehensive charts support.

Here are two examples to illustrate what is available, one simple and one more involved.

> **Add a basic chart**
>
> ```julia
> using XLSX, XLSX.Charts
> 
> f = XLSX.opentemplate("Example_add_chart.xlsx")
> n = length(XLSX.gettable(f\[1\]).data\[1\])
> 
> c = addChart(f\[1\], :column; anchor = "D2:H12", title = false)
> addSeries(c, "B2:B$(n + 1)"; categories = "A2:A$(n + 1)", name_ref = "B1")
> 
> XLSX.writexlsx("Example_add_native_chart_out.xlsx", f, overwrite=true)
> 
> ```

 ![Add_native_chart](https://global.discourse-cdn.com/julialang/original/3X/c/7/c7226af2d0b94e99f84eee0f777f36238c757027.png)

> **Create a more complex chart**
>
> ```julia
> using XLSX, XLSX.Charts
> 
> # 1. The raw data: counts of responses at each level, written as an Excel table
> questions = ["The course objectives were clear",
> "The pace of the course was about right",
> "The course materials were useful",
> "The assessments were fair",
> "I would recommend this course to others",
> "The venue was comfortable"]
> levels = ["Strongly disagree", "Disagree", "Neutral", "Agree", "Strongly agree"]
> counts = [2 5 8 20 15
> 4 10 12 15 7
> 1 3 6 22 18
> 6 12 9 12 6
> 3 6 9 18 14
> 9 12 9 8 4]
> n = length(questions)
> last = n + 1
> 
> xf = XLSX.newxlsx("Survey")
> s = xf["Survey"]
> XLSX.writetable!(s, [questions, collect.(eachcol(counts))...], ["Question"; levels];
> as_table = true, table_name = "responses", table_style = "TableStyleMedium2")
> 
> # 2. Preparation: proportions as Excel formulas, beside the table after an empty column
> headers = ["Neutral (left half)", "Disagree", "Strongly disagree",
> "Neutral (right half)", "Agree", "Strongly agree", "Key"]
> for (col, h) in zip('H':'N', headers)
> s["$(col)1"] = h
> end
> 
> total = "SUM(B2:F2)"
> XLSX.setFormula(s, "H2:H$last", "=-D2/2/$total") # neutral, left half
> XLSX.setFormula(s, "I2:I$last", "=-C2/$total") # disagree
> XLSX.setFormula(s, "J2:J$last", "=-B2/$total") # strongly disagree
> XLSX.setFormula(s, "K2:K$last", "=D2/2/$total") # neutral, right half
> XLSX.setFormula(s, "L2:L$last", "=E2/$total") # agree
> XLSX.setFormula(s, "M2:M$last", "=F2/$total") # strongly agree
> s["N2:N$last"] = 0 # key series: plots nothing
> XLSX.setFormat(s, "H2:M$last"; format = "Percentage")
> 
> # The axis bounds are fixed when the file is written, from the same data
> p = counts ./ sum(counts; dims = 2)
> half = p[:, 3] ./ 2
> left = vec(sum(p[:, 1:2]; dims = 2)) .+ half
> right = vec(sum(p[:, 4:5]; dims = 2)) .+ half
> lo = -ceil(maximum(left) * 5) / 5
> hi = ceil(maximum(right) * 5) / 5
> 
> # 3. The chart
> palette = [:red, :orange, :yellow, :lightgreen, :green]
> cats = "Survey!A2:A$last"
> c = addChart(s, :stackedBar; anchor = "A9:N40", title = "Course feedback")
> 
> # Negative side first, neutral half nearest zero; then the positive side
> plotted = [("H", 3), ("I", 2), ("J", 1), ("K", 3), ("L", 4), ("M", 5)] # column, level
> for (col, level) in plotted
> addSeries(c, "Survey!$(col)2:$(col)$last";
> categories = cats, name = levels[level], color = palette[level])
> end
> 
> # Zero-valued series in natural order, for the legend; hide the data series' entries
> for level in 1:5
> addSeries(c, "Survey!N2:N$last";
> categories = cats, name = levels[level], color = palette[level])
> end
> setLegendEntryDeleted(c, 1:6, true)
> 
> # 4. Formatting
> for i in 1:11
> setSeriesLine(c, i; color = "000000", width = 0.75)
> end
> setGroupGapWidth(c, only(getChartGroups(c)), 50)
> 
> axs = getChartAxes(c)
> catax = only(getChartAxes(c, :category))
> valax = only(getChartAxes(c, :value))
> 
> setAxisOrientation(c, catax, :maxMin) # first question at the top
> setAxisTickLabelPos(c, catax, :low) # labels at the left edge, not on the zero line
> setAxisCrosses(c, valax, :max) # keep the value axis at the bottom
> 
> setAxisScaling(c, valax; min = lo, max = hi)
> setAxisMajorUnit(c, valax, 0.2)
> setAxisNumberFormatCode(c, valax, "0%;0%")
> setAxisTitleText(c, valax, "Proportion of responses")
> 
> setAxisGridlines(c, valax; major = false)
> setPlotAreaLine(c; color = "000000")
> 
> font, black = "Comic Sans MS", "000000"
> for (ax, size, mark) in ((catax, 14, :cross), (valax, 10, :out))
> setAxisLine(c, ax; color = black)
> setAxisMajorTickMark(c, ax, mark)
> setAxisTextProp(c, ax, :size, size)
> setAxisTextProp(c, ax, :font, font)
> setAxisTextProp(c, ax, :color, black)
> end
> 
> setAxisTitleTextProp(c, valax, :size, 12)
> setAxisTitleTextProp(c, valax, :font, font)
> setAxisTitleTextProp(c, valax, :color, black)
> 
> setChartTitleTextProp(c, :size, 18)
> setChartTitleTextProp(c, :font, font)
> setChartTitleTextProp(c, :color, black)
> setLegendTextProp(c, :size, 12)
> setLegendTextProp(c, :font, font)
> 
> XLSX.writexlsx("likert.xlsx", xf; overwrite = true)
> 
> ```

 ![likert](https://global.discourse-cdn.com/julialang/original/3X/1/6/16a2162a2f682b8383c270db4af9218cad4189d0.png)

This functionality has been a major undertaking - there are lots of finicky details in the way Excel handles things. I have relied on Claude to a considerable extent to unravel the details of the OOXML spec and put it to practical use. I have also used it to help write the docs. I have marked the whole feature-set as experimental simply because it is far beyond anything I have done before. I hope the functionality is sufficiently useful to allow you to overlook the AI rough edges. I will, over time (and probably slowly), seek to smooth these out but, in the meantime, I’d welcome feedback, suggestions, issues, PRs, etc, etc.

---

<div class="post-metadata">

**Author:** ![George9000](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/george9000/32/23619_2.png) [@George9000](https://discourse.julialang.org/u/George9000)\
**Post date:** [October 3, 2026, 12:27am UTC](https://discourse.julialang.org/t/ann-xlsx-jl-v0-11-0-now-released-bringing-significant-new-functionality/136345/12 "2026-10-03T00:27:36Z")

</div>

Grateful for all you’ve done with this package!
