Properties API
Cells, columns and rows carry definitions: the editor type, the mask, the alignment, a read-only flag, a width or a height. Version 13 exposes them through three explicit method pairs, one per target, with the same shape. Every setter takes an array of records, so batch operations are native, every change is one undoable operation, and only the records travel to connected users.
What's new with Version 13
- Three explicit pairs:
setCellProperties,setColumnPropertiesandsetRowProperties, with their getters, replace the overloadedsetProperty,getProperty,getOptions,setColumnOptionsandgetColumnOptions- Replace or update: a record replaces the definitions by default;
update: truemerges a subset over the current ones- Row definitions: rows gain the same programmatic API as columns
- Events:
onchangecellproperties,onchangecolumnpropertiesandonchangerowpropertiesreplaceonchangeproperty
Documentation
Methods
| Method | Description |
|---|---|
getCellProperties |
The effective properties of a cell: its own properties, or the column definitions as fallback.getCellProperties(x: Number, y: Number) => Object |
setCellProperties |
Replace the own properties of one or more cells. null or an empty object resets a cell to the column definitions.setCellProperties(records: { x: Number, y: Number, value: Object | null, update?: Boolean }[]) => void |
setCellProperties |
The same operation with a map keyed by cell name.setCellProperties(records: { [cellName: String]: Object | null }) => void |
getColumnProperties |
The column definitions.getColumnProperties(x: Number) => Object |
setColumnProperties |
Replace the definitions of one or more columns. Position, elements and identity are preserved.setColumnProperties(records: { column: Number, value: Object, update?: Boolean }[]) => void |
getRowProperties |
The row definitions.getRowProperties(y: Number) => Object |
setRowProperties |
Replace the definitions of one or more rows. Position, element and identity are preserved.setRowProperties(records: { row: Number, value: Object, update?: Boolean }[]) => void |
Records
| Attribute | Description |
|---|---|
x: number, y: number |
The zero-indexed coordinates, for cell records. |
column: number |
The zero-indexed column, for column records. |
row: number |
The zero-indexed row, for row records. |
value: object | null |
The new definitions. For cells, null or {} resets the cell to its column definitions. |
update?: boolean |
When true, only the given keys are applied over the current definitions, and only that delta travels to connected users. When false or omitted, the object replaces the definitions entirely. |
Records with coordinates beyond the grid or invalid entries are skipped; the valid records of a batch still apply. A batch where nothing applies returns false.
Events
| Event | Description |
|---|---|
onchangecellproperties |
After setCellProperties, with the records that were applied.onchangecellproperties(worksheet: Object, records: Object[]) => void |
onchangecolumnproperties |
After setColumnProperties, with the records that were applied.onchangecolumnproperties(worksheet: Object, records: Object[]) => void |
onchangerowproperties |
After setRowProperties, with the records that were applied.onchangerowproperties(worksheet: Object, records: Object[]) => void |
Replace or update
The setters use replace semantics by default: the object passed becomes the new definitions. To change a subset of properties, add update: true to the record.
// Replace: the cell becomes a dropdown and loses any other own property
worksheet.setCellProperties([
{ x: 0, y: 0, value: { type: 'dropdown', source: ['Yes', 'No'] } },
]);
// Update: only the mask changes, the type and the source stay
worksheet.setCellProperties([
{ x: 0, y: 0, value: { mask: '#.##0,00' }, update: true },
]);
// Reset: the cell follows the column definitions again
worksheet.setCellProperties([
{ x: 0, y: 0, value: null },
]);
Migration from version 12
| Version 12 | Version 13 |
|---|---|
setProperty(x, y, properties) |
setCellProperties([{ x, y, value: properties }]) |
setProperty(x, y, properties, true) |
setCellProperties([{ x, y, value: properties, update: true }]) |
setProperty(records) |
setCellProperties(records) |
getProperty(x, y) / getOptions(x, y) |
getCellProperties(x, y) |
setProperty(x, properties) |
setColumnProperties([{ column: x, value: properties }]) |
setColumnOptions(x, properties) |
setColumnProperties([{ column: x, value: properties }]) |
getProperty(x) / getColumnOptions(x) |
getColumnProperties(x) |
onchangeproperty |
onchangecellproperties, onchangecolumnproperties |
Examples
Cells, columns and rows
This example changes the definitions of a cell, a column and a row, and reads them back.
<html>
<script src="https://jspreadsheet.com/v13/jspreadsheet.js"></script>
<script src="https://jsuites.net/v6/jsuites.js"></script>
<link rel="stylesheet" href="https://jspreadsheet.com/v13/jspreadsheet.css" type="text/css" />
<link rel="stylesheet" href="https://jsuites.net/v6/jsuites.css" type="text/css" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Material+Icons" />
<div id="spreadsheet"></div>
<p>
<input type="button" value="A1 as dropdown" id="btn1" />
<input type="button" value="Column B as calendar" id="btn2" />
<input type="button" value="Row 3 read-only" id="btn3" />
<input type="button" value="Reset A1" id="btn4" />
</p>
<script>
// You can use the following license for quick testing on localhost, StackBlitz, or CodeSandbox.
// The license is valid for one day, after which the spreadsheet will become read-only.
// For a longer trial period, you can create a free account and generate a demo license with an extended expiration date.
jspreadsheet.setLicense('NjljZDM1NWRkMGMxYWU1N2ZiN2ExNjg3NzUyNWEzYzY0OGZmY2VmYTM0NjY4N2I0MzNjNTQ3MjQwMzkwNGRjNDNiZWFkNGNlODc4MTg0ZDE1NzZiMjA3ZDBkMDVlOWE3YmQ1ZWU5OGM0MDMxNzM4MTE2OTBhYTg2NDljMjIzMDUsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpnNE9ESTNOREkyTENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW1Ob1lYSjBjeUlzSW1admNtMXpJaXdpWm05eWJYVnNZU0lzSW5CaGNuTmxjaUlzSW5KbGJtUmxjaUlzSW1OdmJXMWxiblJ6SWl3aWFXMXdiM0owWlhJaUxDSmlZWElpTENKMllXeHBaR0YwYVc5dWN5SXNJbk5sWVhKamFDSXNJbkJ5YVc1MElpd2ljMmhsWlhSeklpd2lZMnhwWlc1MElpd2ljMlZ5ZG1WeUlpd2ljMmhoY0dWeklpd2labTl5YldGMElpd2ljR2wyYjNRaVhTd2laR1Z0YnlJNmRISjFaWDA9');
// Create the spreadsheet
let worksheets = jspreadsheet(document.getElementById('spreadsheet'), {
worksheets: [{
data: [
['Yes', '2026-01-10', 100],
['No', '2026-02-15', 250],
['Yes', '2026-03-20', 180],
],
columns: [
{ title: 'Answer', width: 120 },
{ title: 'Date', width: 140 },
{ title: 'Amount', width: 100, type: 'number' },
],
minDimensions: [3, 4],
}],
onchangecellproperties: function(worksheet, records) {
console.log('cells', records);
},
onchangecolumnproperties: function(worksheet, records) {
console.log('columns', records);
},
onchangerowproperties: function(worksheet, records) {
console.log('rows', records);
},
});
document.getElementById('btn1').onclick = function() {
worksheets[0].setCellProperties([
{ x: 0, y: 0, value: { type: 'dropdown', source: ['Yes', 'No'] } },
]);
console.log(worksheets[0].getCellProperties(0, 0));
}
document.getElementById('btn2').onclick = function() {
worksheets[0].setColumnProperties([
{ column: 1, value: { type: 'calendar', format: 'DD/MM/YYYY' }, update: true },
]);
console.log(worksheets[0].getColumnProperties(1));
}
document.getElementById('btn3').onclick = function() {
worksheets[0].setRowProperties([
{ row: 2, value: { readOnly: true }, update: true },
]);
console.log(worksheets[0].getRowProperties(2));
}
document.getElementById('btn4').onclick = function() {
worksheets[0].setCellProperties([{ x: 0, y: 0, value: null }]);
}
</script>
</html>
import React, { useRef } from "react";
import { Spreadsheet, Worksheet, jspreadsheet } from "@jspreadsheet/react";
import "jsuites/dist/jsuites.css";
import "jspreadsheet/dist/jspreadsheet.css";
// You can use the following license for quick testing on localhost, StackBlitz, or CodeSandbox.
// The license is valid for one day, after which the spreadsheet will become read-only.
// For a longer trial period, you can create a free account and generate a demo license with an extended expiration date.
jspreadsheet.setLicense('NjljZDM1NWRkMGMxYWU1N2ZiN2ExNjg3NzUyNWEzYzY0OGZmY2VmYTM0NjY4N2I0MzNjNTQ3MjQwMzkwNGRjNDNiZWFkNGNlODc4MTg0ZDE1NzZiMjA3ZDBkMDVlOWE3YmQ1ZWU5OGM0MDMxNzM4MTE2OTBhYTg2NDljMjIzMDUsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpnNE9ESTNOREkyTENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW1Ob1lYSjBjeUlzSW1admNtMXpJaXdpWm05eWJYVnNZU0lzSW5CaGNuTmxjaUlzSW5KbGJtUmxjaUlzSW1OdmJXMWxiblJ6SWl3aWFXMXdiM0owWlhJaUxDSmlZWElpTENKMllXeHBaR0YwYVc5dWN5SXNJbk5sWVhKamFDSXNJbkJ5YVc1MElpd2ljMmhsWlhSeklpd2lZMnhwWlc1MElpd2ljMlZ5ZG1WeUlpd2ljMmhoY0dWeklpd2labTl5YldGMElpd2ljR2wyYjNRaVhTd2laR1Z0YnlJNmRISjFaWDA9');
export default function App() {
// Spreadsheet array of worksheets
const spreadsheet = useRef();
// Data
const data = [
['Yes', '2026-01-10', 100],
['No', '2026-02-15', 250],
['Yes', '2026-03-20', 180],
];
// Columns
const columns = [
{ title: 'Answer', width: 120 },
{ title: 'Date', width: 140 },
{ title: 'Amount', width: 100, type: 'number' },
];
const cell = () => spreadsheet.current[0].setCellProperties([
{ x: 0, y: 0, value: { type: 'dropdown', source: ['Yes', 'No'] } },
]);
const column = () => spreadsheet.current[0].setColumnProperties([
{ column: 1, value: { type: 'calendar', format: 'DD/MM/YYYY' }, update: true },
]);
const row = () => spreadsheet.current[0].setRowProperties([
{ row: 2, value: { readOnly: true }, update: true },
]);
const reset = () => spreadsheet.current[0].setCellProperties([{ x: 0, y: 0, value: null }]);
// Render component
return (
<>
<Spreadsheet ref={spreadsheet} onchangecellproperties={(w, records) => console.log(records)}>
<Worksheet data={data} columns={columns} minDimensions={[3, 4]} />
</Spreadsheet>
<input type="button" value="A1 as dropdown" onClick={cell} />
<input type="button" value="Column B as calendar" onClick={column} />
<input type="button" value="Row 3 read-only" onClick={row} />
<input type="button" value="Reset A1" onClick={reset} />
</>
);
}
<template>
<Spreadsheet ref="spreadsheet" :onchangecellproperties="log">
<Worksheet :data="data" :columns="columns" :minDimensions="[3, 4]" />
</Spreadsheet>
<input type="button" value="A1 as dropdown" @click="cell" />
<input type="button" value="Column B as calendar" @click="column" />
<input type="button" value="Row 3 read-only" @click="row" />
<input type="button" value="Reset A1" @click="reset" />
</template>
<script>
import { Spreadsheet, Worksheet, jspreadsheet } from "@jspreadsheet/vue";
import "jsuites/dist/jsuites.css";
import "jspreadsheet/dist/jspreadsheet.css";
// You can use the following license for quick testing on localhost, StackBlitz, or CodeSandbox.
// The license is valid for one day, after which the spreadsheet will become read-only.
// For a longer trial period, you can create a free account and generate a demo license with an extended expiration date.
jspreadsheet.setLicense('NjljZDM1NWRkMGMxYWU1N2ZiN2ExNjg3NzUyNWEzYzY0OGZmY2VmYTM0NjY4N2I0MzNjNTQ3MjQwMzkwNGRjNDNiZWFkNGNlODc4MTg0ZDE1NzZiMjA3ZDBkMDVlOWE3YmQ1ZWU5OGM0MDMxNzM4MTE2OTBhYTg2NDljMjIzMDUsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpnNE9ESTNOREkyTENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW1Ob1lYSjBjeUlzSW1admNtMXpJaXdpWm05eWJYVnNZU0lzSW5CaGNuTmxjaUlzSW5KbGJtUmxjaUlzSW1OdmJXMWxiblJ6SWl3aWFXMXdiM0owWlhJaUxDSmlZWElpTENKMllXeHBaR0YwYVc5dWN5SXNJbk5sWVhKamFDSXNJbkJ5YVc1MElpd2ljMmhsWlhSeklpd2lZMnhwWlc1MElpd2ljMlZ5ZG1WeUlpd2ljMmhoY0dWeklpd2labTl5YldGMElpd2ljR2wyYjNRaVhTd2laR1Z0YnlJNmRISjFaWDA9');
export default {
components: {
Spreadsheet,
Worksheet,
},
data() {
return {
data: [
['Yes', '2026-01-10', 100],
['No', '2026-02-15', 250],
['Yes', '2026-03-20', 180],
],
columns: [
{ title: 'Answer', width: 120 },
{ title: 'Date', width: 140 },
{ title: 'Amount', width: 100, type: 'number' },
],
};
},
methods: {
log(worksheet, records) {
console.log(records);
},
cell() {
this.$refs.spreadsheet.current[0].setCellProperties([
{ x: 0, y: 0, value: { type: 'dropdown', source: ['Yes', 'No'] } },
]);
},
column() {
this.$refs.spreadsheet.current[0].setColumnProperties([
{ column: 1, value: { type: 'calendar', format: 'DD/MM/YYYY' }, update: true },
]);
},
row() {
this.$refs.spreadsheet.current[0].setRowProperties([
{ row: 2, value: { readOnly: true }, update: true },
]);
},
reset() {
this.$refs.spreadsheet.current[0].setCellProperties([{ x: 0, y: 0, value: null }]);
},
}
}
</script>
import { Component, ViewChild, ElementRef } from "@angular/core";
import jspreadsheet from "jspreadsheet";
import "jsuites/dist/jsuites.css";
import "jspreadsheet/dist/jspreadsheet.css";
// You can use the following license for quick testing on localhost, StackBlitz, or CodeSandbox.
// The license is valid for one day, after which the spreadsheet will become read-only.
// For a longer trial period, you can create a free account and generate a demo license with an extended expiration date.
jspreadsheet.setLicense('NjljZDM1NWRkMGMxYWU1N2ZiN2ExNjg3NzUyNWEzYzY0OGZmY2VmYTM0NjY4N2I0MzNjNTQ3MjQwMzkwNGRjNDNiZWFkNGNlODc4MTg0ZDE1NzZiMjA3ZDBkMDVlOWE3YmQ1ZWU5OGM0MDMxNzM4MTE2OTBhYTg2NDljMjIzMDUsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpnNE9ESTNOREkyTENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW1Ob1lYSjBjeUlzSW1admNtMXpJaXdpWm05eWJYVnNZU0lzSW5CaGNuTmxjaUlzSW5KbGJtUmxjaUlzSW1OdmJXMWxiblJ6SWl3aWFXMXdiM0owWlhJaUxDSmlZWElpTENKMllXeHBaR0YwYVc5dWN5SXNJbk5sWVhKamFDSXNJbkJ5YVc1MElpd2ljMmhsWlhSeklpd2lZMnhwWlc1MElpd2ljMlZ5ZG1WeUlpd2ljMmhoY0dWeklpd2labTl5YldGMElpd2ljR2wyYjNRaVhTd2laR1Z0YnlJNmRISjFaWDA9');
// Create component
@Component({
standalone: true,
selector: "app-root",
template: `
<div #spreadsheet></div>
<input type="button" value="A1 as dropdown" (click)="cell()" />
<input type="button" value="Column B as calendar" (click)="column()" />
<input type="button" value="Row 3 read-only" (click)="row()" />
<input type="button" value="Reset A1" (click)="reset()" />
`,
})
export class AppComponent {
@ViewChild("spreadsheet") spreadsheet: ElementRef;
// Worksheets
worksheets: jspreadsheet.worksheetInstance[];
// Create a new data grid
ngAfterViewInit() {
// Create spreadsheet
this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {
worksheets: [{
data: [
['Yes', '2026-01-10', 100],
['No', '2026-02-15', 250],
['Yes', '2026-03-20', 180],
],
columns: [
{ title: 'Answer', width: 120 },
{ title: 'Date', width: 140 },
{ title: 'Amount', width: 100, type: 'number' },
],
minDimensions: [3, 4],
}],
onchangecellproperties: (worksheet, records) => console.log(records),
});
}
cell() {
this.worksheets[0].setCellProperties([
{ x: 0, y: 0, value: { type: 'dropdown', source: ['Yes', 'No'] } },
]);
}
column() {
this.worksheets[0].setColumnProperties([
{ column: 1, value: { type: 'calendar', format: 'DD/MM/YYYY' }, update: true },
]);
}
row() {
this.worksheets[0].setRowProperties([
{ row: 2, value: { readOnly: true }, update: true },
]);
}
reset() {
this.worksheets[0].setCellProperties([{ x: 0, y: 0, value: null }]);
}
}