Skip to content

Read-only cells

Make specified cells read-only to protect them from unwanted changes but still allow navigation and copying of data.

Disable individual cells, entire columns, or entire rows to prevent user edits. Use readOnly on cells, columns, or the whole grid.

Overview

Disabling a cell makes the cell read-only or non-editable. Both have similar outcomes, with the following differences:

Read-only cell
readOnly: true
Non-editable cell
editor: false
Has an additional CSS class (htDimmed)Has no additional CSS class
Copy works, paste doesn’t workCopy-paste works
Drag-to-fill doesn’t workDrag-to-fill works
Can’t be changed by populateFromArray()Can be changed by populateFromArray()

Make the grid read-only

To make the entire grid read-only, set readOnly to true as a top-level grid option.

JavaScript
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
return (
<HotTable
data={[
{ car: 'Tesla', year: 2017, chassis: 'black', bumper: 'black' },
{ car: 'Nissan', year: 2018, chassis: 'blue', bumper: 'blue' },
{ car: 'Chrysler', year: 2019, chassis: 'yellow', bumper: 'black' },
{ car: 'Volvo', year: 2020, chassis: 'white', bumper: 'gray' },
]}
height="auto"
colHeaders={['Car', 'Year', 'Chassis color', 'Bumper color']}
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
readOnly={true}
/>
);
};
export default ExampleComponent;
TypeScript
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
return (
<HotTable
data={[
{ car: 'Tesla', year: 2017, chassis: 'black', bumper: 'black' },
{ car: 'Nissan', year: 2018, chassis: 'blue', bumper: 'blue' },
{ car: 'Chrysler', year: 2019, chassis: 'yellow', bumper: 'black' },
{ car: 'Volvo', year: 2020, chassis: 'white', bumper: 'gray' },
]}
height="auto"
colHeaders={['Car', 'Year', 'Chassis color', 'Bumper color']}
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
readOnly={true}
/>
);
};
export default ExampleComponent;

Make a column read-only

To make a column read-only, declare it in the columns configuration option. The column remains available for keyboard navigation and copying data (Ctrl/Cmd+C), but editing and pasting are disabled. The example below also defines a custom renderer that dims the read-only column, giving the user a visual cue that its cells are read-only.

JavaScript
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
import { textRenderer } from 'handsontable/renderers/textRenderer';
// register Handsontable's modules
registerAllModules();
const dimmedTextRenderer = (instance, td, ...rest) => {
textRenderer(instance, td, ...rest);
td.style.opacity = '0.6';
};
const ExampleComponent = () => {
return (<HotTable data={[
{ car: 'Tesla', year: 2017, chassis: 'black', bumper: 'black' },
{ car: 'Nissan', year: 2018, chassis: 'blue', bumper: 'blue' },
{ car: 'Chrysler', year: 2019, chassis: 'yellow', bumper: 'black' },
{ car: 'Volvo', year: 2020, chassis: 'white', bumper: 'gray' },
]} height="auto" colHeaders={['Car', 'Year', 'Chassis color', 'Bumper color']} autoWrapRow={true} autoWrapCol={true} licenseKey="non-commercial-and-evaluation" columns={[
{
data: 'car',
readOnly: true,
renderer: dimmedTextRenderer,
},
{
data: 'year',
},
{
data: 'chassis',
},
{
data: 'bumper',
},
]}/>);
};
export default ExampleComponent;
TypeScript
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
import { BaseRenderer } from 'handsontable/renderers';
import { textRenderer } from 'handsontable/renderers/textRenderer';
// register Handsontable's modules
registerAllModules();
const dimmedTextRenderer: BaseRenderer = (instance, td, ...rest) => {
textRenderer(instance, td, ...rest);
td.style.opacity = '0.6';
};
const ExampleComponent = () => {
return (
<HotTable
data={[
{ car: 'Tesla', year: 2017, chassis: 'black', bumper: 'black' },
{ car: 'Nissan', year: 2018, chassis: 'blue', bumper: 'blue' },
{ car: 'Chrysler', year: 2019, chassis: 'yellow', bumper: 'black' },
{ car: 'Volvo', year: 2020, chassis: 'white', bumper: 'gray' },
]}
height="auto"
colHeaders={['Car', 'Year', 'Chassis color', 'Bumper color']}
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
columns={[
{
data: 'car',
readOnly: true,
renderer: dimmedTextRenderer,
},
{
data: 'year',
},
{
data: 'chassis',
},
{
data: 'bumper',
},
]}
/>
);
};
export default ExampleComponent;

Make a row read-only

To make an entire row read-only, use the cells function and set the readOnly property based on the row index, regardless of the column. The example below makes the second row (index 1) read-only.

JavaScript
import { useRef, useEffect } from 'react';
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
const hotRef = useRef(null);
useEffect(() => {
const hot = hotRef.current?.hotInstance;
hot?.updateSettings({
cells(row) {
return row === 1 ? { readOnly: true } : {};
},
});
});
return (<HotTable ref={hotRef} data={[
{ car: 'Tesla', year: 2017, chassis: 'black', bumper: 'black' },
{ car: 'Nissan', year: 2018, chassis: 'blue', bumper: 'blue' },
{ car: 'Chrysler', year: 2019, chassis: 'yellow', bumper: 'black' },
{ car: 'Volvo', year: 2020, chassis: 'white', bumper: 'gray' },
]} colHeaders={['Car', 'Year', 'Chassis color', 'Bumper color']} height="auto" autoWrapRow={true} autoWrapCol={true} licenseKey="non-commercial-and-evaluation"/>);
};
export default ExampleComponent;
TypeScript
import { useRef, useEffect } from 'react';
import { HotTable, HotTableRef } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
const hotRef = useRef<HotTableRef>(null);
useEffect(() => {
const hot = hotRef.current?.hotInstance;
hot?.updateSettings({
cells(row) {
return row === 1 ? { readOnly: true } : {};
},
});
});
return (
<HotTable
ref={hotRef}
data={[
{ car: 'Tesla', year: 2017, chassis: 'black', bumper: 'black' },
{ car: 'Nissan', year: 2018, chassis: 'blue', bumper: 'blue' },
{ car: 'Chrysler', year: 2019, chassis: 'yellow', bumper: 'black' },
{ car: 'Volvo', year: 2020, chassis: 'white', bumper: 'gray' },
]}
colHeaders={['Car', 'Year', 'Chassis color', 'Bumper color']}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;

Make specific cells read-only

To make specific cells read-only, use the cells function to set the readOnly property conditionally. The example below makes cells that contain the word “Nissan” read-only.

JavaScript
import { useRef, useEffect } from 'react';
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
const hotRef = useRef(null);
useEffect(() => {
const hot = hotRef.current?.hotInstance;
hot?.updateSettings({
cells(row, col) {
const cellProperties = {};
if (hot.getData()[row][col] === 'Nissan') {
cellProperties.readOnly = true;
}
return cellProperties;
},
});
});
return (
<HotTable
ref={hotRef}
data={[
{ car: 'Tesla', year: 2017, chassis: 'black', bumper: 'black' },
{ car: 'Nissan', year: 2018, chassis: 'blue', bumper: 'blue' },
{ car: 'Chrysler', year: 2019, chassis: 'yellow', bumper: 'black' },
{ car: 'Volvo', year: 2020, chassis: 'white', bumper: 'gray' },
]}
colHeaders={['Car', 'Year', 'Chassis color', 'Bumper color']}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;
TypeScript
import { useRef, useEffect } from 'react';
import Handsontable from 'handsontable/base';
import { HotTable, HotTableRef } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
const hotRef = useRef<HotTableRef>(null);
useEffect(() => {
const hot = hotRef.current?.hotInstance;
hot?.updateSettings({
cells(row, col) {
const cellProperties: Handsontable.CellMeta = {};
if (hot.getData()[row][col] === 'Nissan') {
cellProperties.readOnly = true;
}
return cellProperties;
},
});
});
return (
<HotTable
ref={hotRef}
data={[
{ car: 'Tesla', year: 2017, chassis: 'black', bumper: 'black' },
{ car: 'Nissan', year: 2018, chassis: 'blue', bumper: 'blue' },
{ car: 'Chrysler', year: 2019, chassis: 'yellow', bumper: 'black' },
{ car: 'Volvo', year: 2020, chassis: 'white', bumper: 'gray' },
]}
colHeaders={['Car', 'Year', 'Chassis color', 'Bumper color']}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;

Non-editable cells behave like any other cells apart from preventing you from manually changing their values.

Make a column non-editable

To make a column non-editable, declare it in the columns configuration option. The column’s basic behavior does not change — you can still use keyboard navigation, Ctrl/Cmd+C, Ctrl/Cmd+V, and drag-to-fill. The example below also defines a custom renderer that dims the non-editable column, giving the user a visual cue that its cells are non-editable.

JavaScript
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
import { textRenderer } from 'handsontable/renderers/textRenderer';
// register Handsontable's modules
registerAllModules();
const dimmedTextRenderer = (instance, td, ...rest) => {
textRenderer(instance, td, ...rest);
td.style.opacity = '0.6';
};
const ExampleComponent = () => {
return (<HotTable data={[
{ car: 'Tesla', year: 2017, chassis: 'black', bumper: 'black' },
{ car: 'Nissan', year: 2018, chassis: 'blue', bumper: 'blue' },
{ car: 'Chrysler', year: 2019, chassis: 'yellow', bumper: 'black' },
{ car: 'Volvo', year: 2020, chassis: 'white', bumper: 'gray' },
]} height="auto" colHeaders={['Car', 'Year', 'Chassis color', 'Bumper color']} autoWrapRow={true} autoWrapCol={true} licenseKey="non-commercial-and-evaluation" columns={[
{
data: 'car',
editor: false,
renderer: dimmedTextRenderer,
},
{
data: 'year',
editor: 'numeric',
},
{
data: 'chassis',
editor: 'text',
},
{
data: 'bumper',
editor: 'text',
},
]}/>);
};
export default ExampleComponent;
TypeScript
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
import { BaseRenderer } from 'handsontable/renderers';
import { textRenderer } from 'handsontable/renderers/textRenderer';
// register Handsontable's modules
registerAllModules();
const dimmedTextRenderer: BaseRenderer = (instance, td, ...rest) => {
textRenderer(instance, td, ...rest);
td.style.opacity = '0.6';
};
const ExampleComponent = () => {
return (
<HotTable
data={[
{ car: 'Tesla', year: 2017, chassis: 'black', bumper: 'black' },
{ car: 'Nissan', year: 2018, chassis: 'blue', bumper: 'blue' },
{ car: 'Chrysler', year: 2019, chassis: 'yellow', bumper: 'black' },
{ car: 'Volvo', year: 2020, chassis: 'white', bumper: 'gray' },
]}
height="auto"
colHeaders={['Car', 'Year', 'Chassis color', 'Bumper color']}
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
columns={[
{
data: 'car',
editor: false,
renderer: dimmedTextRenderer,
},
{
data: 'year',
editor: 'numeric',
},
{
data: 'chassis',
editor: 'text',
},
{
data: 'bumper',
editor: 'text',
},
]}
/>
);
};
export default ExampleComponent;

Make specific cells non-editable

To make specific cells non-editable, set editor: false in the cell configuration. The following example shows a table with non-editable cells containing the word “Nissan”.

JavaScript
import { useRef, useEffect } from 'react';
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
const hotRef = useRef(null);
useEffect(() => {
const hot = hotRef.current?.hotInstance;
hot?.updateSettings({
cells(row, _col, prop) {
const cellProperties = {};
if (hot.getDataAtRowProp(row, prop) === 'Nissan') {
cellProperties.editor = false;
} else {
cellProperties.editor = 'text';
}
return cellProperties;
},
});
});
return (
<HotTable
ref={hotRef}
data={[
{ car: 'Tesla', year: 2017, chassis: 'black', bumper: 'black' },
{ car: 'Nissan', year: 2018, chassis: 'blue', bumper: 'blue' },
{ car: 'Chrysler', year: 2019, chassis: 'yellow', bumper: 'black' },
{ car: 'Volvo', year: 2020, chassis: 'white', bumper: 'gray' },
]}
colHeaders={['Car', 'Year', 'Chassis color', 'Bumper color']}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;
TypeScript
import { useRef, useEffect } from 'react';
import Handsontable from 'handsontable/base';
import { HotTable, HotTableRef } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ExampleComponent = () => {
const hotRef = useRef<HotTableRef>(null);
useEffect(() => {
const hot = hotRef.current?.hotInstance;
hot?.updateSettings({
cells(row, _col, prop) {
const cellProperties: Handsontable.CellMeta = {};
if (hot.getDataAtRowProp(row, prop as string) === 'Nissan') {
cellProperties.editor = false;
} else {
cellProperties.editor = 'text';
}
return cellProperties;
},
});
});
return (
<HotTable
ref={hotRef}
data={[
{ car: 'Tesla', year: 2017, chassis: 'black', bumper: 'black' },
{ car: 'Nissan', year: 2018, chassis: 'blue', bumper: 'blue' },
{ car: 'Chrysler', year: 2019, chassis: 'yellow', bumper: 'black' },
{ car: 'Volvo', year: 2020, chassis: 'white', bumper: 'gray' },
]}
colHeaders={['Car', 'Year', 'Chassis color', 'Bumper color']}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
};
export default ExampleComponent;

Accessibility

When ariaTags is enabled (the default), Handsontable adds aria-readonly="true" to the DOM element of every read-only cell, so screen readers announce that the cell can’t be edited. Non-editable cells (editor: false) don’t get this attribute, because Handsontable doesn’t treat them as read-only in the data model — only their editor is disabled.

For more accessibility features and testing guidance, see Accessibility.

Result

Read-only cells display with the htDimmed CSS class and block paste and drag-to-fill operations. Non-editable cells block manual editing but allow copy-paste and drag-to-fill.

Configuration options