- You save column widths to localStorage on
afterColumnResize(e.g.{linkedTask: 42, ...}). - Verified in DevTools: localStorage value updates correctly after every resize.
- On
F5reload, widths revert to the defaults defined incolWidthsarray.
The manualColumnResize plugin keeps its own internal map of widths,
separate from the colWidths constructor option.
When you initialize Handsontable with:
new Handsontable(container, {
colWidths: [110, 80, 100, ...], // base widths (physical order)
manualColumnResize: true // <-- plugin enabled, internal map is EMPTY
});…the plugin sees true and infers "no saved manual widths". On the first
re-render after loadData() / updateSettings() the plugin's empty
internal map wins and the columns snap back to whatever colWidths
provides — which is the default in your code, not the persisted value.
colWidths only acts as a base width. Manual resize values must be passed
to the plugin as an array through the manualColumnResize option
itself.
afterColumnResize(newSize, column /* visual */) {
const physical = this.toPhysicalColumn(column);
const key = COLUMNS[physical]?.key;
if (key && newSize) {
saveToLocalStorage(key, newSize);
}
}When reading widths back from a live grid (e.g. for a "Save settings" button),
read by visual index — getColWidth(physical) returns the wrong cell after
a manualColumnMove:
function getColumnWidths() {
const widths = {};
COLUMNS.forEach((col, physical) => {
const visual = hot.toVisualColumn(physical);
if (visual >= 0) widths[col.key] = hot.getColWidth(visual);
});
return widths;
}Pass the saved widths into BOTH colWidths and manualColumnResize:
function buildArr(savedWidths) {
return COLUMNS.map((col, idx) => savedWidths[col.key] || allColumns[idx].width);
}
function buildManualSetting(savedWidths) {
const arr = COLUMNS.map(col => savedWidths[col.key]); // no defaults
return arr.some(w => w !== undefined) ? arr : true;
}
// Initial:
new Handsontable(container, {
colWidths: buildArr(saved),
manualColumnResize: buildManualSetting(saved),
// ...
});
// After applyColumnOrder() / updateSettings({manualColumnMove}):
hot.updateSettings({
colWidths: buildArr(saved),
manualColumnResize: buildManualSetting(saved)
});Without the second branch, switching column order via manualColumnMove
clears the internal width map again.
stretchH: 'all' will auto-redistribute widths on every render and override
manual resize. Use stretchH: 'none' when you want strict persisted widths.
- The Handsontable docs treat
colWidthsandmanualColumnResizeas separate options without explicitly calling out that the plugin maintains its own width state. - Devtools shows localStorage saving correctly, so debugging looks like a load-side issue when the load API itself is being silently overridden.
- Svelte 4
- Handsontable ^14