For the complete documentation index, see llms.txt. This page is also available as Markdown.
Version comparison
The version comparison feature allows users to compare two versions of a spreadsheet and visualize differences inline. Changes are highlighted with distinct colors for added, deleted, and modified cells.
Spreadsheet version comparison UI
Overview
Version comparison is useful for:
Tracking changes between document revisions
Reviewing edits before accepting them
Understanding what changed between snapshots
Collaboration workflows where multiple users edit the same document
Visual Highlighting
Change Type
Background
Text Color
Description
Added
Light green (#d0fae1)
Dark green (#046e38)
Cell exists in current version but not in previous
Deleted
Light red (#ffdbdb)
Dark red (#b21313)
Cell exists in previous version but not in current (with strikethrough)
Modified
Both colors
Red for old, green for new
Cell value or format changed
Modified Cells
When a cell is modified, both the old and new values are displayed side by side within the cell:
Old value: Red background with the previous formatting applied
New value: Green background with the current formatting applied
This applies to both value changes and format-only changes (e.g., text becoming bold).
When a cell's value stays the same but formatting changes (e.g., text becomes bold):
Working with Shared Strings
If your cell data uses shared strings (ss property pointing to a shared strings map), pass the shared strings:
Cell data with shared string:
If the previous version has a different shared strings map (e.g., from a snapshot):
Resolving Effective Formats
Format resolution is caller-driven. The hook never inspects ef.sid / uf.sid style references and never touches a cellXfs registry — it just compares the CellFormat objects you hand back. That keeps the hook agnostic to whatever resolution chain your app uses (cellXfs sid lookup, runtime overlays from a cell style store, conditional formatting, derived formats, …).
Pass getEffectiveFormat / getPreviousEffectiveFormat to opt in to format comparison:
If both sides share a resolver, pass only getEffectiveFormat — getPreviousEffectiveFormat defaults to it. If you omit both, format changes simply aren't detected — only value changes will show up in the diff.
Cell data with a style reference looks like:
Comparison Summary
To display a summary of all changes:
CanvasGrid Props
Prop
Type
Description
isComparing
boolean
Enable comparison mode rendering
getCellDiff
(sheetId, row, col) => CellDiff | null
Function to get cell diff
Diff Engine Functions
For advanced use cases, you can use the diff engine functions directly:
Best Practices
Snapshot Management: Store snapshots efficiently - consider only storing changed cells rather than the entire sheet.
Performance: For large spreadsheets, consider lazy-loading diffs only for visible cells.
User Experience:
Show a clear indicator when comparison mode is active
Provide a summary of changes
Allow users to navigate between changes
Format Comparison: Remember that format changes count as modifications even if the value is unchanged.
type UseVersionComparisonOptions<T extends CellData = CellData> = {
/** Function to get current cell data */
getCellData: (sheetId: number, rowIndex: number, columnIndex: number) => T | null | undefined;
/** Function to get previous cell data (null = not comparing) */
getPreviousCellData: ((sheetId: number, rowIndex: number, columnIndex: number) => T | null | undefined) | null;
/**
* Resolved effective `CellFormat` for the current version. The caller owns
* all resolution — inline `ef` / `uf`, sid → cellXfs lookup, cellStyleStore
* overlays, conditional formatting, etc. If omitted, format changes are
* not detected (only value changes contribute to the diff).
*/
getEffectiveFormat?: (sheetId: number, rowIndex: number, columnIndex: number) => CellFormat | null | undefined;
/**
* Resolved effective `CellFormat` for the previous version. Defaults to
* `getEffectiveFormat` if omitted (useful when both sides share a resolver).
*/
getPreviousEffectiveFormat?: (sheetId: number, rowIndex: number, columnIndex: number) => CellFormat | null | undefined;
/** Shared strings map for current version */
sharedStrings?: Map<string, string> | null;
/** Shared strings map for previous version (defaults to sharedStrings) */
previousSharedStrings?: Map<string, string> | null;
};
const diff = getCellDiff(sheetId, rowIndex, columnIndex);
const isFormatOnlyChange = diff?.detail?.formatChanged && !diff?.detail?.valueChanged;
if (isFormatOnlyChange) {
// Show both old and new side by side to display formatting difference
}