2022-09-02 07:16:24 +00:00
|
|
|
---
|
2024-04-01 10:44:10 +00:00
|
|
|
title: Cell Comments and Notes
|
|
|
|
|
sidebar_label: Cell Comments
|
2022-09-02 07:16:24 +00:00
|
|
|
sidebar_position: 4
|
|
|
|
|
---
|
|
|
|
|
|
2025-11-16 07:08:21 +00:00
|
|
|
import Tabs from '@theme/Tabs';
|
|
|
|
|
import TabItem from '@theme/TabItem';
|
|
|
|
|
|
2024-04-08 04:47:04 +00:00
|
|
|
<details>
|
|
|
|
|
<summary><b>File Format Support</b> (click to show)</summary>
|
2022-09-02 07:16:24 +00:00
|
|
|
|
2023-06-13 17:49:52 +00:00
|
|
|
Comments and Notes have evolved over the years.
|
|
|
|
|
|
|
|
|
|
Excel 2.0 - '95 "Notes" were displayed in a master list.
|
|
|
|
|
|
2025-02-24 01:17:05 +00:00
|
|
|
Excel '97 - 2024 "Comments" float over the sheet and support styling.
|
2022-09-02 07:16:24 +00:00
|
|
|
|
2023-05-15 08:38:23 +00:00
|
|
|
Excel 365 introduced "Threaded Comments" which do not support rich text but do
|
|
|
|
|
allow users to "reply". The original "Comments" were renamed to "Notes".
|
|
|
|
|
|
|
|
|
|
| Formats | Notes | Comment | Threaded |
|
|
|
|
|
|:------------------|:-----:|:-------:|:--------:|
|
2023-06-13 17:49:52 +00:00
|
|
|
| XLSX / XLSM | ✕ | ✔ | ✔ |
|
|
|
|
|
| XLSB | ✕ | R | R |
|
2023-06-14 19:32:34 +00:00
|
|
|
| NUMBERS | ✕ | ✕ | ✔ |
|
2023-06-13 17:49:52 +00:00
|
|
|
| XLS (BIFF8) | ✕ | ✔ | ✕ |
|
|
|
|
|
| XLML | ✕ | ✔ | ✕ |
|
|
|
|
|
| ODS / FODS / UOS | ✕ | ✔ | ✕ |
|
|
|
|
|
| SYLK | ✔ | ✕ | ✕ |
|
|
|
|
|
| XLS (BIFF5) | ✔ | ✕ | ✕ |
|
|
|
|
|
| XLS (BIFF 2/3/4) | ✔ | ✕ | ✕ |
|
|
|
|
|
|
|
|
|
|
X (✕) marks features that are not supported by the file formats. For example,
|
|
|
|
|
the NUMBERS file format supports plaintext threaded comments but does not
|
|
|
|
|
support Excel styled comments or Excel legacy notes.
|
2023-05-15 08:38:23 +00:00
|
|
|
|
|
|
|
|
The letter R (R) marks features parsed but not written in the format.
|
2022-09-02 07:16:24 +00:00
|
|
|
|
2025-11-16 07:08:21 +00:00
|
|
|
:::tip pass
|
2023-06-13 17:49:52 +00:00
|
|
|
|
|
|
|
|
[SheetJS Pro](https://sheetjs.com/pro) supports comment rich text and styling.
|
|
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
2022-09-02 07:16:24 +00:00
|
|
|
</details>
|
|
|
|
|
|
2024-04-01 10:44:10 +00:00
|
|
|
Comments and notes are cell annotations. Cells with comments or notes are marked
|
2025-11-16 07:08:21 +00:00
|
|
|
with `◥` or `¬` in the upper-right corner.
|
2024-04-01 10:44:10 +00:00
|
|
|
|
|
|
|
|
Excel notes are standalone text boxes with adjustable background colors and
|
|
|
|
|
support for rich text. Historically people "replied" to comments by adding text
|
|
|
|
|
to the end of existing comments.
|
|
|
|
|
|
|
|
|
|
Excel comments are simple text boxes that allow users to enter plain text. Users
|
2025-11-16 07:08:21 +00:00
|
|
|
can reply to comments. The replies are typically shown in a vertical stack.
|
|
|
|
|
|
|
|
|
|
#### Live Example
|
2024-04-01 10:44:10 +00:00
|
|
|
|
2025-11-16 07:08:21 +00:00
|
|
|
The following live example shows a spreadsheet with comments and a note.
|
2024-04-01 10:44:10 +00:00
|
|
|
|
|
|
|
|
- The note is associated with cell A1 (the cell with the red triangle). It has
|
|
|
|
|
a green gradient background fill.
|
|
|
|
|
- The comments are associated with cell A2 (the cell with the blue `¬`). There
|
|
|
|
|
are 2 comments from different authors. A "Reply" box appears below the thread.
|
|
|
|
|
|
2025-11-16 07:08:21 +00:00
|
|
|
:::caution pass
|
|
|
|
|
|
|
|
|
|
Excel for Mac and other software that lack full support for threaded comments
|
|
|
|
|
will show a fallback note that includes each reply on a separate line.
|
|
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
2024-04-01 10:44:10 +00:00
|
|
|

|
|
|
|
|
|
2025-11-16 07:08:21 +00:00
|
|
|
<details open>
|
|
|
|
|
<summary><b>Live Example</b> (click to hide)</summary>
|
|
|
|
|
|
|
|
|
|
```jsx live
|
|
|
|
|
function SheetJSThreadedComments() {
|
|
|
|
|
return ( <button onClick={() => {
|
|
|
|
|
var ws = XLSX.utils.aoa_to_sheet([["SheetJS"], [5433795]], {dense: true});
|
|
|
|
|
var cell_A1 = (ws["!data"][0]??=[])[0]??={t:"z"}; // Cell A1
|
|
|
|
|
var cell_A2 = (ws["!data"][1]??=[])[0]??={t:"z"}; // Cell A2
|
|
|
|
|
|
|
|
|
|
/* normal comment in cell A1 */
|
|
|
|
|
cell_A1.c ??= [];
|
|
|
|
|
cell_A1.c.push({a:"SheetJS", t:"This is not threaded"});
|
|
|
|
|
|
|
|
|
|
/* threaded comment in cell A2 */
|
|
|
|
|
cell_A2.c ??= [];
|
|
|
|
|
|
|
|
|
|
/* add parts */
|
|
|
|
|
cell_A2.c.push({a:"SheetJS", t:"This is threaded", T: true});
|
|
|
|
|
|
|
|
|
|
var part = {a:"JSSheet", t:"This is also threaded"};
|
|
|
|
|
cell_A2.c.push({...part, T: true});
|
|
|
|
|
|
|
|
|
|
/* create workbook and export */
|
|
|
|
|
var wb = XLSX.utils.book_new(ws, "Sheet1");
|
|
|
|
|
XLSX.writeFile(wb, "SheetJSThreadedComments.xlsx");
|
|
|
|
|
}}>Click me to generate a sample file</button> );
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
</details>
|
|
|
|
|
|
|
|
|
|
:::info Application Support
|
2024-04-01 10:44:10 +00:00
|
|
|
|
|
|
|
|
Google Sheets "notes" do not currently support rich text or background colors.
|
|
|
|
|
|
|
|
|
|
Apple Numbers supports "comments" but does not support "notes".
|
|
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
2022-09-02 07:16:24 +00:00
|
|
|
## Basic Structure
|
|
|
|
|
|
2025-11-16 07:08:21 +00:00
|
|
|
Cell comments are stored in the `c` property of cell objects. They are expected
|
|
|
|
|
to be arrays of fragments objects that include text and metadata.
|
2022-09-02 07:16:24 +00:00
|
|
|
|
|
|
|
|
For example, the following snippet appends a cell comment into cell `A1`:
|
|
|
|
|
|
2025-11-16 07:08:21 +00:00
|
|
|
<Tabs groupId="sheetrep">
|
|
|
|
|
<TabItem value="sparse" label="Sparse Sheets">
|
|
|
|
|
|
|
|
|
|
```js title="Add cell comment to cell A1 in a sparse worksheet"
|
2023-06-14 19:32:34 +00:00
|
|
|
/* get cell A1, creating an empty cell if necessary */
|
2022-09-02 07:16:24 +00:00
|
|
|
var cell = ws["A1"];
|
2025-11-16 07:08:21 +00:00
|
|
|
if(!cell) cell = ws["A1"] = { t: "z" };
|
2022-09-02 07:16:24 +00:00
|
|
|
|
|
|
|
|
/* create comment array if it does not exist */
|
2023-06-13 17:49:52 +00:00
|
|
|
if(!cell.c) cell.c = [];
|
2022-09-02 07:16:24 +00:00
|
|
|
|
|
|
|
|
/* create a comment part */
|
|
|
|
|
var comment_part = {
|
2023-06-13 17:49:52 +00:00
|
|
|
a: "SheetJS",
|
|
|
|
|
t: "I'm a little comment, short and stout!"
|
2022-09-02 07:16:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Add comment part to the comment array */
|
|
|
|
|
cell.c.push(comment_part);
|
|
|
|
|
```
|
|
|
|
|
|
2025-11-16 07:08:21 +00:00
|
|
|
</TabItem>
|
|
|
|
|
<TabItem value="dense" label="Dense Sheets">
|
|
|
|
|
|
|
|
|
|
:::info pass
|
|
|
|
|
|
|
|
|
|
[`read`](/docs/api/parse-options), [`aoa_to_sheet`](/docs/api/utilities/array),
|
|
|
|
|
and other SheetJS methods will generate sparse worksheets by default.
|
|
|
|
|
|
|
|
|
|
[Dense mode worksheets](/docs/csf/sheet#dense-mode) require explicit opt-in.
|
|
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
|
|
```js title="Add cell comment to cell A1 in a dense worksheet"
|
|
|
|
|
/* get cell A1, creating an empty cell if necessary */
|
|
|
|
|
var row = ws["!data"][0];
|
|
|
|
|
if(!row) row = ws["!data"][0] = [];
|
|
|
|
|
var cell = row[0];
|
|
|
|
|
if(!row[0]) cell = row[0] = { t: "z" };
|
|
|
|
|
|
|
|
|
|
/* create comment array if it does not exist */
|
|
|
|
|
if(!cell.c) cell.c = [];
|
|
|
|
|
|
|
|
|
|
/* create a comment part */
|
|
|
|
|
var comment_part = {
|
|
|
|
|
a: "SheetJS",
|
|
|
|
|
t: "I'm a little comment, short and stout!"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Add comment part to the comment array */
|
|
|
|
|
cell.c.push(comment_part);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
</TabItem>
|
|
|
|
|
</Tabs>
|
|
|
|
|
|
|
|
|
|
### Fragment Properties
|
|
|
|
|
|
|
|
|
|
Fragments support the following properties:
|
|
|
|
|
|
|
|
|
|
| Key | Description |
|
|
|
|
|
|:----|:-------------------------------------------------------------------|
|
|
|
|
|
| `t` | [Text](#fragment-text) |
|
|
|
|
|
| `a` | [Author](#fragment-author) |
|
|
|
|
|
| `T` | If true, fragment is a [threaded comment](#threaded-comments) part |
|
|
|
|
|
|
|
|
|
|
#### Fragment Text
|
|
|
|
|
|
|
|
|
|
Each fragment must include text.
|
|
|
|
|
|
|
|
|
|
Note and comment fragment texts are concatenated to form the full text.
|
|
|
|
|
|
|
|
|
|
[Threaded comment](#threaded-comments) fragments are visually distinct.
|
|
|
|
|
|
|
|
|
|
:::tip pass
|
|
|
|
|
|
|
|
|
|
SheetJS CE supports plaintext comments.
|
|
|
|
|
|
|
|
|
|
[SheetJS Pro](https://sheetjs.com/pro) supports comment styling and rich text.
|
|
|
|
|
|
|
|
|
|
Google Sheets and Excel threaded comments currently do not support styling.
|
|
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
|
|
#### Fragment Author
|
|
|
|
|
|
|
|
|
|
The fragment author is optional.
|
|
|
|
|
|
|
|
|
|
Note and comment fragment authors are not displayed in Excel.
|
|
|
|
|
|
|
|
|
|
[Threaded comment](#threaded-comments) fragment authors are displayed in the
|
|
|
|
|
thread in Excel.
|
|
|
|
|
|
2022-09-02 07:16:24 +00:00
|
|
|
:::note XLSB Author limits
|
|
|
|
|
|
|
|
|
|
XLSB enforces a 54 character limit on the Author name. Names longer than 54
|
|
|
|
|
characters may cause issues with other formats.
|
|
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
2025-11-16 07:08:21 +00:00
|
|
|
### Comment Properties
|
|
|
|
|
|
|
|
|
|
#### Visibility
|
|
|
|
|
|
|
|
|
|
The `hidden` property of the comment block indicates comment visibility. If set
|
|
|
|
|
to `true`, the comment will not be visible until users hover over the comment.
|
|
|
|
|
|
|
|
|
|
```js title="Mark a cell comment as hidden"
|
|
|
|
|
if(!cell.c) cell.c = [];
|
|
|
|
|
// highlight-next-line
|
|
|
|
|
cell.c.hidden = true;
|
|
|
|
|
cell.c.push({a:"SheetJS", t:"This comment will be hidden"});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
<details>
|
|
|
|
|
<summary><b>Live Example</b> (click to show)</summary>
|
|
|
|
|
|
|
|
|
|
The following demo creates a worksheet with two comments. The comment in cell A1
|
|
|
|
|
will be visibile and the comment in cell A2 will be hidden.
|
|
|
|
|
|
|
|
|
|
```jsx live
|
|
|
|
|
function SheetJSComments2() {
|
|
|
|
|
return (<button onClick={() => {
|
|
|
|
|
var ws = XLSX.utils.aoa_to_sheet([["SheetJS"], [5433795]]);
|
|
|
|
|
|
|
|
|
|
if(!ws.A1.c) ws.A1.c = [];
|
|
|
|
|
ws.A1.c.push({a:"SheetJS", t:"This comment is visible"});
|
|
|
|
|
|
|
|
|
|
if(!ws.A2.c) ws.A2.c = [];
|
|
|
|
|
ws.A2.c.hidden = true;
|
|
|
|
|
ws.A2.c.push({a:"SheetJS", t:"This comment will be hidden"});
|
|
|
|
|
|
|
|
|
|
var wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
|
|
|
|
|
XLSX.writeFile(wb, "SheetJSComments2.xlsx");
|
|
|
|
|
}}>Click me to generate a sample file</button>);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
</details>
|
|
|
|
|
|
2023-09-24 03:59:48 +00:00
|
|
|
## Demos
|
|
|
|
|
|
|
|
|
|
#### Export
|
|
|
|
|
|
2024-04-08 04:47:04 +00:00
|
|
|
<details open>
|
|
|
|
|
<summary><b>Live Export Example</b> (click to hide)</summary>
|
2023-04-27 09:12:19 +00:00
|
|
|
|
|
|
|
|
This example creates a small worksheet with a comment in cell A1:
|
2022-09-02 07:16:24 +00:00
|
|
|
|
|
|
|
|
```jsx live
|
|
|
|
|
function SheetJSComments1() {
|
|
|
|
|
return (<button onClick={() => {
|
|
|
|
|
var ws = XLSX.utils.aoa_to_sheet([["SheetJS"]]);
|
|
|
|
|
|
|
|
|
|
if(!ws.A1.c) ws.A1.c = [];
|
|
|
|
|
ws.A1.c.push({a:"SheetJS", t:"I'm a little comment, short and stout!"});
|
|
|
|
|
|
|
|
|
|
var wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
|
|
|
|
|
XLSX.writeFile(wb, "SheetJSComments1.xlsx");
|
|
|
|
|
}}>Click me to generate a sample file</button>);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
</details>
|
|
|
|
|
|
2023-09-24 03:59:48 +00:00
|
|
|
#### Import
|
|
|
|
|
|
2024-04-08 04:47:04 +00:00
|
|
|
<details>
|
|
|
|
|
<summary><b>Live Import Example</b> (click to show)</summary>
|
2023-04-27 09:12:19 +00:00
|
|
|
|
|
|
|
|
This example displays every comment in the workbook:
|
|
|
|
|
|
|
|
|
|
```jsx live
|
|
|
|
|
function SheetJSParseComments(props) {
|
|
|
|
|
const [__html, setHTML] = React.useState("");
|
|
|
|
|
|
|
|
|
|
return ( <>
|
|
|
|
|
<input type="file" onChange={async(e) => {
|
|
|
|
|
/* parse workbook */
|
|
|
|
|
const file = e.target.files[0];
|
|
|
|
|
const data = await file.arrayBuffer();
|
|
|
|
|
const wb = XLSX.read(data);
|
|
|
|
|
|
|
|
|
|
const html = [];
|
|
|
|
|
wb.SheetNames.forEach(n => {
|
|
|
|
|
var ws = wb.Sheets[n]; if(!ws) return;
|
|
|
|
|
var ref = XLSX.utils.decode_range(ws["!ref"]);
|
|
|
|
|
for(var R = 0; R <= ref.e.r; ++R) for(var C = 0; C <= ref.e.c; ++C) {
|
|
|
|
|
var addr = XLSX.utils.encode_cell({r:R,c:C});
|
|
|
|
|
if(!ws[addr] || !ws[addr].c) continue;
|
|
|
|
|
var comments = ws[addr].c;
|
|
|
|
|
if(!comments.length) continue;
|
|
|
|
|
var threaded = !!comments[0].T;
|
|
|
|
|
var msg = comments.map(c => c.t).join(threaded ? "\n" : "");
|
|
|
|
|
console.log(comments);
|
|
|
|
|
html.push(`${n}:${addr}:${+!!threaded}:${msg}`);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
setHTML(html.join("\n"));
|
|
|
|
|
}}/>
|
|
|
|
|
<pre dangerouslySetInnerHTML={{ __html }}/>
|
|
|
|
|
</> );
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
</details>
|
|
|
|
|
|
2022-09-02 07:16:24 +00:00
|
|
|
## Threaded Comments
|
|
|
|
|
|
2023-06-14 19:32:34 +00:00
|
|
|
Threaded comments are plain text comment snippets with author metadata and
|
|
|
|
|
parent references. They are supported in XLSX, XLSB, and NUMBERS files.
|
2022-09-02 07:16:24 +00:00
|
|
|
|
|
|
|
|
To mark a comment as threaded, each comment part must have a true `T` property:
|
|
|
|
|
|
2025-11-16 07:08:21 +00:00
|
|
|
```js title="Create a threaded comment with a reply"
|
2022-09-02 07:16:24 +00:00
|
|
|
if(!cell.c) cell.c = [];
|
|
|
|
|
|
|
|
|
|
var part1 = {
|
|
|
|
|
a:"SheetJS",
|
|
|
|
|
t:"This is threaded",
|
|
|
|
|
// highlight-next-line
|
|
|
|
|
T: true
|
|
|
|
|
};
|
|
|
|
|
cell.c.push(part1);
|
|
|
|
|
|
|
|
|
|
var part2 = {
|
|
|
|
|
a:"JSSheet",
|
|
|
|
|
t:"This is also threaded",
|
|
|
|
|
};
|
|
|
|
|
// The next line uses Object Spread syntax to add T: true
|
|
|
|
|
// highlight-next-line
|
|
|
|
|
cell.c.push({ ...part2, T: true});
|
|
|
|
|
```
|
|
|
|
|
|
2025-11-16 07:08:21 +00:00
|
|
|
:::caution pass
|
2022-09-02 07:16:24 +00:00
|
|
|
|
2025-11-16 07:08:21 +00:00
|
|
|
There is no Active Directory or Office 365 metadata associated with authors.
|
2022-09-02 07:16:24 +00:00
|
|
|
|
2025-11-16 07:08:21 +00:00
|
|
|
:::
|