Column Properties
File Format Support (click to show)
Many spreadsheet tools support adjusting column widths to accommodate longer formatted data or varying text sizes.
Some tools additionally support column grouping or "outlining". Excel displays outline levels above the grid.
SheetJS worksheet objects store column properties in the !cols field. It is
expected to be an array of column metadata objects.
For most common formats (XLSX, XLS), widths are tied to font metrics, which are tied to Windows Scaling settings. In Windows 11, the Scale factor settings are found in "System" > "Display" > "Scale"
Column widths may appear different on other machines due to scaling.
This is an issue with Excel.
Demo​
This example creates a workbook that includes custom column widths, hidden columns, and column outline levels.
| Excel for Windows | Excel for Mac |
|---|---|
|
|
Export Demo (click to show)
Functions​
Column processing must be explicitly enabled!
Functions creating worksheet objects are not guaranteed to generate the !cols
array. Writers are not guaranteed to export column metadata.
Reading Files​
read and readFile accept an options argument. The
cellStyles option must be set to true to generate column properties:
var wb = XLSX.read(data, {/* ... other options , */ cellStyles: true});
Writing Files​
write and writeFile accept an options argument.
The cellStyles option must be set to true to export column properties:
XLSX.writeFile(wb, "SheetSColProps.xlsx", {/* ...opts , */ cellStyles: true});
Exporting Data​
sheet_to_csv and
sheet_to_json accept options. If the
skipHidden option is set to true, hidden columns will not be exported:
var ws = wb.Sheets[wb.SheetNames[0]]; // first worksheet
var csv = XLSX.utils.sheet_to_csv(ws, {/* ...opts, */ skipHidden: true});
Storage​
The !cols property in a sheet object stores column-level metadata. If present,
it is expected to be an array of column objects.
As explained in "Addresses and Ranges", SheetJS uses
zero-indexed columns. The column metadata for Excel column "T" is stored at index
19 of the !cols array.
When performing operations, it is strongly recommended to test for the existence of the column structure.
This snippet checks the !cols array and the specific column object, creating
them if they do not exist, before setting the hidden property of column "C":
/* Excel column "C" -> SheetJS column index 2 == XLSX.utils.decode_col("C") */
var COL_INDEX = 2;
/* create !cols array if it does not exist */
if(!ws["!cols"]) ws["!cols"] = [];
/* create column metadata object if it does not exist */
if(!ws["!cols"][COL_INDEX]) ws["!cols"][COL_INDEX] = {wch: 8};
/* set column to hidden */
ws["!cols"][COL_INDEX].hidden = true;
Column Widths​
Column widths can be specified in three ways:
| Property | Description | Excel UI |
|---|---|---|
wpx | Width in screen pixels | Pixels |
wch | "inner width" in MDW ** | Width |
width | "outer width" in MDW ** |
When resizing a column, Excel will show a tooltip:

wpx stores the "pixels" field (65 in the diagram) for certain computer and
font settings.
MDW (Max Digit Width) (click to show)
The following snippet sets the width of column "C" to 50 pixels:
const COL_WIDTH = 50;
/* Excel column "C" -> SheetJS column index 2 == XLSX.utils.decode_col("C") */
var COL_INDEX = 2;
/* create !cols array if it does not exist */
if(!ws["!cols"]) ws["!cols"] = [];
/* create column metadata object if it does not exist */
if(!ws["!cols"][COL_INDEX]) ws["!cols"][COL_INDEX] = {wch: 8};
/* set column width */
ws["!cols"][COL_INDEX].wpx = COL_WIDTH;
Column Visibility​
The hidden property controls visibility.
The following snippet hides column "D":
/* Excel column "D" -> SheetJS column index 3 == XLSX.utils.decode_col("D") */
var COL_INDEX = 3;
/* create !cols array if it does not exist */
if(!ws["!cols"]) ws["!cols"] = [];
/* create column metadata object if it does not exist */
if(!ws["!cols"][COL_INDEX]) ws["!cols"][COL_INDEX] = {wch: 8};
/* set column to hidden */
ws["!cols"][COL_INDEX].hidden = true;
Outline Levels​
The level property controls outline level / grouping. It is expected to be a
number between 0 and 7 inclusive.
The Excel UI displays outline levels above the row labels. The base level
shown in the application is 1.
SheetJS is zero-indexed: the default (base) level is 0.
The following snippet sets the level of column "F" to Excel 2 / SheetJS 1:
/* Excel level 2 -> SheetJS level 2 - 1 = 1 */
var LEVEL = 1;
/* Excel column "F" -> SheetJS column index 5 == XLSX.utils.decode_col("F") */
var COL_INDEX = 5;
/* create !cols array if it does not exist */
if(!ws["!cols"]) ws["!cols"] = [];
/* create column metadata object if it does not exist */
if(!ws["!cols"][COL_INDEX]) ws["!cols"][COL_INDEX] = {wch: 8};
/* set level */
ws["!cols"][COL_INDEX].level = LEVEL;
Grouping Columns​
Applications treat consecutive columns with the same level as part of a "group".
The "Group" command typically increments the level of each column in the range:
/* start_col and end_col are SheetJS 0-indexed column indices */
function grouper(ws, start_col, end_col) {
/* create !cols array if it does not exist */
if(!ws["!cols"]) ws["!cols"] = [];
/* loop over every column index */
for(var i = start_col; i <= end_col; ++i) {
/* create column metadata object if it does not exist */
if(!ws["!cols"][i]) ws["!cols"][i] = {wch: 8};
/* increment level */
ws["!cols"][i].level = 1 + (ws["!cols"][i].level || 0);
}
}
The "Ungroup" command typically decrements the level of each column in the range:
/* start_col and end_col are SheetJS 0-indexed column indices */
function aufheben(ws, start_col, end_col) {
/* create !cols array if it does not exist */
if(!ws["!cols"]) ws["!cols"] = [];
/* loop over every column index */
for(var i = start_col; i <= end_col; ++i) {
/* if column metadata does not exist, the level is zero -> skip */
if(!ws["!cols"][i]) continue;
/* if column level is not specified, the level is zero -> skip */
if(!ws["!cols"][i].level) continue;
/* decrement level */
--ws["!cols"][i].level;
}
}
Grouping Symbol​
By default, Excel displays the group collapse button on the column after the data. In the UI, this option is named "Summary columns to right of detail".
SheetJS exposes this option in the left property of the "!outline" property
of worksheet objects. Setting this property to true effectively "unchecks" the
"Summary columns to right of detail" option in Excel:
if(!ws["outline"]) ws["!outline"] = {};
ws["!outline"].left = true; // show summary to left of detail


