Add getCellStyle escape hatch for custom per-cell styling - #8
Conversation
Adds an optional `getCellStyle` viewer prop that returns CSS overrides merged on top of each cell's resolved style. This is a generic escape hatch for per-cell styling (highlights, outlines, status tints) without forking the workbook data. The hook is applied at the single getCellData chokepoint so both the DOM and canvas renderers honor it, and is wired into the cell render cache invalidation so changing the callback re-resolves and repaints cells. The callback receives an XlsxCellStyleContext with the cell address, sheet, resolved style, value, and flags (validation, hyperlink, conditional format, chart highlight, merge, table header). Also adds a "Highlight" toggle to the playground demonstrating the prop and documents it in both READMEs. Co-authored-by: Cursor <cursoragent@cursor.com>
|
@tothienbao6a0 is attempting to deploy a commit to the Extend Team on Vercel. A member of the Team first needs to authorize it. |
|
Downstream, extend-hq/ui#15 (draft) exposes |
|
Opening this up for discussion 👋 Motivation: when embedding the viewer in a host app, there's currently no first-class way to layer app-driven styling (row highlights, selection outlines, status tints, etc.) onto cells without forking the component or post-processing the DOM. A few things I tried to get right:
Open questions for maintainers:
Happy to adjust to fit how you'd want this to look long-term. |
Conditional color-scale fills are stored on a separate field that both the DOM and canvas renderers prioritize over the resolved cell style, so a background returned from getCellStyle was visually ignored on color-scale cells. When getCellStyle returns an explicit background (backgroundColor or background), clear the color-scale fill so the host override wins — both target the full-cell background. Data bars and icon sets are overlays and are left intact. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Great catch, thank you — that's not intended, it was a gap. Root cause: conditional color-scale fills aren't part of the resolved cell style — they're carried on a separate Fix (pushed in d7e25a1): when One scope question for you: I deliberately left data bars and icon sets intact, since those read as overlays/glyphs drawn on top of the fill rather than background fills (a background override replacing them seemed surprising). If you'd rather |
|
yeah, i think data bars and icons should not be covered by cell styles, at least thats what i would expect |
|
Thanks for merging, and agreed — data bars and icon sets staying put matches what's implemented here, so we're aligned on the semantics. 👍 One quick follow-up: is there a release planned that'll include this? I'd like to pin the downstream registry-component PR (extend-hq/ui#15) to the exact version that ships |
just released 0.11.0 on npm ! |

Summary
Adds an optional
getCellStyleprop toXlsxViewer(andXlsxViewerProps) — a generic escape hatch for styling individual cells without forking the workbook data. It follows the same philosophy as the existing render-prop / color-override props (renderImage,renderTableHeaderMenu,selectionColor, etc.).The viewer calls
getCellStylefor every rendered cell and merges the returned partialReact.CSSPropertieson top of the cell's resolved style. Returningundefined/nullleaves a cell untouched.Why
Integrations often need to decorate specific cells (highlights, outlines, status tints) on top of the workbook's own formatting. Today that requires mutating workbook data or forking the renderer.
getCellStyleprovides a small, unopinionated hook for it.Implementation
getCellDatachokepoint that feeds both the DOM and canvas renderers, so styling works in either mode and the canvas style cache is built from the final merged style.getCellDatadependencies, so changing the callback identity re-resolves and repaints cells. The DOM row memo already re-renders ongetCellDataidentity change.XlsxCellStyleContext:cell,workbookSheetIndex,sheetName,resolvedStyle,value, and flags (hasValidation,hasHyperlink,hasConditionalFormat,hasChartHighlight,isMerged,isTableHeader).Renderer parity
experimentalCanvas={false}) applies every returned CSS property.backgroundColor,backgroundImagegradients,color, the fourborder*sides,padding,textAlign,textDecoration,textOverflow, and font properties. CSS-only effects (boxShadow,outline,animation) apply in the DOM renderer. This is documented.useXlsxViewerThumbnails(...).Changes
getCellStyleprop +XlsxCellStyleContexttype, exported from the packageXlsxViewerInner→XlsxGrid→getCellDataTest plan
pnpm typecheck(package + playground)pnpm build(package) —getCellStyleandXlsxCellStyleContextpresent indist/index.d.tsMade with Cursor