Cell Overlays
An overlay is a DOM node of yours that the viewer places on a cell or a range. The viewer keeps it there through scrolling, resizing and sheet switches. You decide what is inside it: an agent’s cursor, a collaborator’s selection, a note with a button.
Overlays are UI only. They are not part of the workbook and are never saved.
The API is the same as the editor’s, so overlay code written for one works with the other.
Interactive Demo
Section titled “Interactive Demo”Overlays live on the controller. Create an element and hand it to overlays.add with a target. The id it returns is what update and remove take.
import { useRef } from "react";import { SpreadsheetViewer } from "@grid-is/spreadsheet-viewer";import type { SpreadsheetViewerController } from "@grid-is/spreadsheet-viewer";
function App() { const controllerRef = useRef<SpreadsheetViewerController>(null);
const addCursor = () => { const element = document.createElement("div"); element.style.border = "2px solid #7c3aed"; controllerRef.current?.overlays.add({ target: "B2", element }); };
return ( <> <button onClick={addCursor}>Mark B2</button> <SpreadsheetViewer model={model} controllerRef={controllerRef} /> </> );}A target is a cell or a range, with or without a sheet name: "B2", "B2:D4", "Summary!B2".
The element
Section titled “The element”The viewer sets these styles on your element and nothing else:
position,left,top,widthandheight, so the element covers its targetpointer-events, from theinteractiveoption
While its target cannot be shown, the element is taken out of the DOM. It is put back as soon as the target can be shown again.
Your content does not have to fit inside the target. The element covers its cells, and a name tag above a cursor or a card beside a cell can overflow it freely. Overflowing content is clipped at the edges of the grid, so it slides under the headers the same way cells do.
In React, render into the element with a portal:
import { useEffect, useState } from "react";import { createPortal } from "react-dom";
function Cursor({ controllerRef, name }) { const [element] = useState(() => document.createElement("div"));
useEffect(() => { const overlays = controllerRef.current?.overlays; if (!overlays) return; const id = overlays.add({ target: "B2", element }); return () => overlays.remove(id); }, [controllerRef, element]);
return createPortal(<span className="name-tag">{name}</span>, element);}Render the component after the viewer has mounted, so the controller is there when the effect runs.
interactive
Section titled “interactive”By default an overlay is a highlight. Clicks, double-clicks and drags go through it to the cells underneath.
Set interactive: true when the overlay has buttons or inputs. Clicks then stop at the overlay and the sheet does not react to them. The mouse wheel scrolls the grid either way.
controllerRef.current?.overlays.add({ target: "I5", element, interactive: true });Review notes
Section titled “Review notes”The note in the demo above is an interactive overlay on the cell. Resolve removes the overlay:
const resolve = () => { controllerRef.current?.overlays.remove(id);};update
Section titled “update”update(id: string, patch: Partial<SpreadsheetOverlay>): voidChanges an overlay. Use it to move an overlay to another target. An id that does not exist is ignored.
controllerRef.current?.overlays.update(id, { target: "D6" });remove
Section titled “remove”remove(id: string): voidTakes the overlay off the sheet. Your element is left as it is.
controllerRef.current?.overlays.remove(id);all(): Array<SpreadsheetOverlay & { id: string }>Returns every overlay with its id, in the order they were added.
const targets = controllerRef.current?.overlays.all().map((overlay) => overlay.target);clear(): voidRemoves every overlay.
controllerRef.current?.overlays.clear();Good to know
Section titled “Good to know”- Sheets. A target with a sheet name, like
"Summary!B2", is out of the DOM while another sheet is open. A target without one, like"B2", shows on whichever sheet is open. - Merged cells. A target is taken as written.
"B2"covers only B2’s own slot, also when B2 is part of a merged cell. Pass the whole range, like"B2:D4", to cover the merged cell. - Hidden rows and columns. The viewer draws hidden rows and columns at their default size, so an overlay on one stays in place.
- A new model. Overlays stay when the
modelprop changes, so an overlay you keep updating carries on working. Callclear, or remove the ones you own, when you load another workbook.
API reference
Section titled “API reference”The types come from @grid-is/editor-types, shared with the editor: