Skip to content

Save data after each change to the data set, using Handsontable’s API hooks. Preserve the table’s state by saving data to the local storage.

Save changes using a callback

To track changes made in your data grid, use Handsontable’s afterChange hook.

The example below handles data by using fetch. Note that this is just a mockup, and nothing is actually saved. You need to implement the server-side part by yourself.

Click “Load” to load data from server

JavaScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// Register all Handsontable's modules.
registerAllModules();
const container = document.querySelector('#example1');
const exampleConsole = document.querySelector('#output');
const autosave = document.querySelector('#autosave');
const load = document.querySelector('#load');
const save = document.querySelector('#save');
const hot = new Handsontable(container, {
startRows: 8,
startCols: 6,
rowHeaders: true,
colHeaders: true,
height: 'auto',
licenseKey: 'non-commercial-and-evaluation',
afterChange(change, source) {
if (source === 'loadData') {
return; // don't save this change
}
if (!change) {
return;
}
if (!autosave.checked) {
return;
}
fetch('/docs/scripts/json/save.json', {
method: 'POST',
mode: 'no-cors',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ data: change }),
}).then(() => {
exampleConsole.innerText = `Autosaved (${change.length} cell${change.length > 1 ? 's' : ''})`;
console.log('The POST request is only used here for the demo purposes');
});
},
autoWrapRow: true,
autoWrapCol: true,
});
load.addEventListener('click', () => {
fetch('/docs/scripts/json/load.json').then((response) => {
response.json().then((data) => {
hot.loadData(data.data);
// or, use `updateData()` to replace `data` without resetting states
exampleConsole.innerText = 'Data loaded';
});
});
});
save.addEventListener('click', () => {
// save all cell's data
fetch('/docs/scripts/json/save.json', {
method: 'POST',
mode: 'no-cors',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ data: hot.getData() }),
}).then(() => {
exampleConsole.innerText = 'Data saved';
console.log('The POST request is only used here for the demo purposes');
});
});
autosave.addEventListener('click', () => {
if (autosave.checked) {
exampleConsole.innerText = 'Changes will be autosaved';
} else {
exampleConsole.innerText = 'Changes will not be autosaved';
}
});
TypeScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// Register all Handsontable's modules.
registerAllModules();
const container = document.querySelector<HTMLDivElement>('#example1')!;
const exampleConsole = document.querySelector<HTMLDivElement>('#output')!;
const autosave = document.querySelector<HTMLInputElement>('#autosave')!;
const load = document.querySelector<HTMLButtonElement>('#load')!;
const save = document.querySelector<HTMLButtonElement>('#save')!;
const hot = new Handsontable(container, {
startRows: 8,
startCols: 6,
rowHeaders: true,
colHeaders: true,
height: 'auto',
licenseKey: 'non-commercial-and-evaluation',
afterChange(change: Handsontable.CellChange[] | null, source: Handsontable.ChangeSource) {
if (source === 'loadData') {
return; // don't save this change
}
if (!change) {
return;
}
if (!autosave.checked) {
return;
}
fetch('/docs/scripts/json/save.json', {
method: 'POST',
mode: 'no-cors',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ data: change }),
}).then(() => {
exampleConsole.innerText = `Autosaved (${change.length} cell${change.length > 1 ? 's' : ''})`;
console.log('The POST request is only used here for the demo purposes');
});
},
autoWrapRow: true,
autoWrapCol: true,
});
load.addEventListener('click', () => {
fetch('/docs/scripts/json/load.json').then((response) => {
response.json().then((data) => {
hot.loadData(data.data);
// or, use `updateData()` to replace `data` without resetting states
exampleConsole.innerText = 'Data loaded';
});
});
});
save.addEventListener('click', () => {
// save all cell's data
fetch('/docs/scripts/json/save.json', {
method: 'POST',
mode: 'no-cors',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ data: hot.getData() }),
}).then(() => {
exampleConsole.innerText = 'Data saved';
console.log('The POST request is only used here for the demo purposes');
});
});
autosave.addEventListener('click', () => {
if (autosave.checked) {
exampleConsole.innerText = 'Changes will be autosaved';
} else {
exampleConsole.innerText = 'Changes will not be autosaved';
}
});
HTML
<div class="example-controls-container">
<div class="controls">
<button id="load" class="button button--primary button--blue">Load data</button>
<button id="save" class="button button--primary button--blue">Save data</button>
<label>
<input type="checkbox" name="autosave" id="autosave"/>
Autosave
</label>
</div>
<output class="console" id="output">Click "Load" to load data from server</output>
</div>
<div id="example1"></div>
CSS
.controls {
display: flex;
align-items: center;
flex-wrap: wrap;
}

Save data locally

To persist table state (e.g. column order, column widths, row order) across page reloads, use the browser’s localStorage API or sessionStorage in your application. Listen to the appropriate hooks (e.g. afterColumnMove, afterColumnResize) and save or restore state as needed.

Core methods

Hooks