-
Notifications
You must be signed in to change notification settings - Fork 136
Description
Description
🐞 Bug Description
I am experiencing significant performance issues when rendering large datasets (500+ rows and ~20+ columns) using Handsontable Community Edition v15.2.0. The table becomes extremely sluggish during rendering, scrolling, and editing, especially on mid-range systems and browsers like Chrom
🔢 Handsontable Version
handsontable: 15.2.0
@handsontable/react-wrapper: 15.2.0
(also tested with handsontable@16.0.1, same issue)
🧪 Steps to Reproduce
1.Initialize Handsontable with a dataset of 500+ rows and 20+ columns.
2.Include features like:
-
Cell styles (bgColor, fontWeight, etc.)
-
Merged cells (mergeCells)
-
Conditional formatting
-
React wrapper (@handsontable/react-wrapper)
3.Try to scroll vertically or horizontally.
4.Try to enter or edit data in random cells.
⚙️ Configuration Snippet
` <HotTable
ref={hotRef}
data={sheetData[selectedSheet]}
mergeCells={mergedCells[selectedSheet] || []}
rowHeaders={true}
colHeaders={true}
height="auto"
width="100%"
autoWrapRow={true}
autoWrapCol={true}
colWidths={100}
manualColumnResize={false}
manualRowResize={false}
viewportRowRenderingOffset={20}
viewportColumnRenderingOffset={10}
licenseKey="non-commercial-and-evaluation"
selectionMode="multiple"
formulas={{
engine: hyperFormulaInstance,
sheetName: selectedSheet,
}}
dropdownMenu={false}
filters={false}
outsideClickDeselects={false}
dragToScroll={true}
copyable={true}
copyMode="with-column-group-headers"
className="htCenter"
virtualized={true}
afterChange={afterChange}
beforeChange={(changes, source) => {
if (source === "undo" || source === "redo") {
const hot = hotRef.current?.hotInstance;
changes.forEach(([row, col]) => {
const meta = hot.getCellMeta(row, col);
if (meta.originalStyle) {
hot.setCellMeta(row, col, "style", {
...meta.originalStyle,
});
}
});
}
return true;
}}
afterMergeCells={(cellRange, mergeParent, auto) => {
if (!auto) {
setMergedCells((prevMergedCells) => {
return {
...prevMergedCells,
[selectedSheet]: [
...(prevMergedCells[selectedSheet] || []),
mergeParent,
],
};
});
setIsDirty(true);
}
}}
afterUnmergeCells={(cellRange, auto) => {
if (!auto) {
const { from } = cellRange;
setMergedCells((prev) => {
const updated = (prev[selectedSheet] || []).filter(
(cell) => !(cell.row === from.row && cell.col === from.col)
);
return {
...prev,
[selectedSheet]: updated,
};
});
setIsDirty(true);
}
}}
afterSetCellMeta={(row, col, key, value) => {
handleSetCellMeta(row, col, key, value);
}}
cells={(row, col) => {
try {
const sheetStyleObj = cellStyles[selectedSheet] || {};
const basicStyles = Object.values(sheetStyleObj).filter(
(item) => typeof item === "object" && item?.row !== undefined && item?.col !== undefined
);
const generalStyle = basicStyles.find((item) => item.row === row && item.col === col);
const conditionalStyles = sheetStyleObj.conditionalFormatting || [];
const conditionStyle = conditionalStyles.find((item) => item.row === row && item.col === col);
const highlighted = highlightedCells[selectedSheet] || [];
const isHighlighted = highlighted.includes(`${row}-${col}`);
return {
className: generalStyle?.className || '',
renderer: function (
instance,
td,
row,
col,
prop,
value,
cellProperties
) {
Handsontable.renderers.TextRenderer.apply(this, arguments);
const sheetValue = instance.getDataAtCell(row, col);
const numericValue = Number(sheetValue);
const isValidNumber = !isNaN(numericValue);
const meta = instance.getCellMeta(row, col);
const precision = meta.decimalPrecision ?? decimalPrecisionMap[selectedSheet]?.[`${row}-${col}`];
const roundedValue = (typeof precision === "number")
? parseFloat(numericValue.toFixed(precision))
: numericValue;
td.innerText = isValidNumber
? (typeof precision === "number" ? roundedValue.toFixed(precision) : String(roundedValue))
: String(sheetValue ?? "");
if (sheetValue === null || sheetValue === undefined || sheetValue === "") {
td.innerText = "";
} else if (isValidNumber) {
td.innerText = typeof precision === "number"
? numericValue.toFixed(precision)
: String(numericValue);
} else {
td.innerText = String(sheetValue);
}
let conditionPassed = false;
if (conditionStyle?.condition && isValidNumber) {
const { operator, value: ruleValue } = conditionStyle.condition;
const ruleNumber = parseFloat(ruleValue);
if (!isNaN(ruleNumber)) {
switch (operator) {
case "=": conditionPassed = roundedValue === ruleNumber; break;
case ">": conditionPassed = roundedValue > ruleNumber; break;
case "<": conditionPassed = roundedValue < ruleNumber; break;
case ">=": conditionPassed = roundedValue >= ruleNumber; break;
case "<=": conditionPassed = roundedValue <= ruleNumber; break;
case "!=": conditionPassed = roundedValue !== ruleNumber; break;
default: conditionPassed = false;
}
}
}
if (conditionPassed && conditionStyle) {
if (conditionStyle.backgroundColor) td.style.backgroundColor = conditionStyle.backgroundColor;
if (conditionStyle.fontColor) td.style.color = conditionStyle.fontColor;
td.style.fontWeight = conditionStyle.bold ? "bold" : "";
}
if (generalStyle) {
if (generalStyle.backgroundColor) td.style.backgroundColor = generalStyle.backgroundColor;
if (generalStyle.fontColor) td.style.color = generalStyle.fontColor;
td.style.fontWeight = generalStyle.bold ? "bold" : "";
}
if (isHighlighted) {
td.style.outline = "2px dashed #ff9900";
td.style.outlineOffset = "-2px";
td.style.backgroundColor = "rgba(255, 255, 0, 0.3)";
}
td.style.overflow = "visible";
td.style.textOverflow = "clip";
},
};
} catch (error) {
console.error(error);
}
}}
afterSelection={function (row, col, row2, column2) {
handleCellSelection(row, col);
const hot = hotRef.current?.hotInstance;
const getDataType = hot?.getDataType();
}}
contextMenu={true}
fillHandle={true}
/>`
🖥️ Expected Behavior
Handsontable should handle 500–1000 rows with acceptable performance for:
Initial rendering
Scrolling
Editing input
🐢 Actual Behavior
Initial render takes 2–5 seconds
Typing/editing has a visible input lag
Scrolling is jerky and unresponsive
The lag increases significantly with more merges/styles
🧠 Additional Context
Using conditional styling with cells() or cell meta
Attempted optimization like virtual scrolling or batch rendering (not available in CE)
Tried removing features — marginal improvement
Video or screenshots
No response
Demo
null
HyperFormula version
3.0.0
Your framework
React
Your environment
Chrome ,Windows 11