docs.sheetjs.com/docz/docs/07-csf/07-features/04-comments.md

362 lines
10 KiB
Markdown
Raw Normal View History

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';
<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
![Excel comments and notes](pathname:///comments/types.png)
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
<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
<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
:::