Empty Data State
Use the EmptyDataState plugin to display a contextual overlay when the grid has no data or all rows are hidden by active filters.
Prerequisites
To use the Empty Data State plugin, import it from Handsontable:
To use the filter-aware empty state (which automatically detects when all rows are hidden by filters), also enable the Filters plugin alongside emptyDataState.
Overview
The Empty Data State plugin provides a user-friendly overlay system for Handsontable when there’s no data to display. It automatically detects when your table is empty or when all data is hidden by filters, and displays an appropriate message with optional action buttons. It automatically integrates with the Filters plugin to provide context-aware messages and actions.
Basic configuration
To enable the Empty Data State plugin, set the emptyDataState option to true or provide a configuration object.
import React, { useRef } from 'react';import { HotTable } from '@handsontable/react-wrapper';import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modulesregisterAllModules();
const ExampleComponent = () => { const hotTableRef = useRef(null);
return ( <HotTable ref={hotTableRef} data={[]} // Empty data to trigger empty state height="auto" colHeaders={true} rowHeaders={true} navigableHeaders={true} dropdownMenu={true} filters={true} emptyDataState={true} // Enable empty data state with default settings licenseKey="non-commercial-and-evaluation" /> );};
export default ExampleComponent;import React, { useRef } from 'react';import { HotTable } from '@handsontable/react-wrapper';import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modulesregisterAllModules();
const ExampleComponent = () => { const hotTableRef = useRef(null);
return ( <HotTable ref={hotTableRef} data={[]} // Empty data to trigger empty state height="auto" colHeaders={true} rowHeaders={true} navigableHeaders={true} dropdownMenu={true} filters={true} emptyDataState={true} // Enable empty data state with default settings licenseKey="non-commercial-and-evaluation" /> );};
export default ExampleComponent;Custom configuration
The empty data state supports customization of the title, description, and action buttons.
import React, { useRef } from 'react';import { HotTable } from '@handsontable/react-wrapper';import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modulesregisterAllModules();
const ExampleComponent = () => { const hotTableRef = useRef(null);
return ( <HotTable ref={hotTableRef} data={[]} // Empty data to trigger empty state height="auto" colHeaders={['First Name', 'Last Name', 'Email']} rowHeaders={true} navigableHeaders={true} dropdownMenu={true} filters={true} emptyDataState={{ message: { title: 'No data available', description: 'Please add some data to get started.', buttons: [ { text: 'Add Sample Data', type: 'primary', callback: () => { // Add some sample data hotTableRef.current?.hotInstance.loadData([ ['John', 'Doe', 'john@example.com'], ['Jane', 'Smith', 'jane@example.com'], ['Bob', 'Johnson', 'bob@example.com'], ['Alice', 'Johnson', 'alice@example.com'], ]); }, }, ], }, }} licenseKey="non-commercial-and-evaluation" /> );};
export default ExampleComponent;import React, { useRef } from 'react';import { HotTable } from '@handsontable/react-wrapper';import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modulesregisterAllModules();
const ExampleComponent = () => { const hotTableRef = useRef(null);
return ( <HotTable ref={hotTableRef} data={[]} // Empty data to trigger empty state height="auto" colHeaders={['First Name', 'Last Name', 'Email']} rowHeaders={true} navigableHeaders={true} dropdownMenu={true} filters={true} emptyDataState={{ message: { title: 'No data available', description: 'Please add some data to get started.', buttons: [ { text: 'Add Sample Data', type: 'primary', callback: () => { // Add some sample data hotTableRef.current?.hotInstance.loadData([ ['John', 'Doe', 'john@example.com'], ['Jane', 'Smith', 'jane@example.com'], ['Bob', 'Johnson', 'bob@example.com'], ['Alice', 'Johnson', 'alice@example.com'], ]); }, }, ], }, }} licenseKey="non-commercial-and-evaluation" /> );};
export default ExampleComponent;Dynamic messages based on source
You can provide different messages based on the source of the empty state (e.g., filters vs. no data). This allows for more contextual user guidance.
import React, { useRef } from 'react';import { HotTable } from '@handsontable/react-wrapper';import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modulesregisterAllModules();
const ExampleComponent = () => { const hotTableRef = useRef(null);
return ( <HotTable ref={hotTableRef} data={[]} // Empty data to trigger empty state height="auto" colHeaders={['First Name', 'Last Name', 'Email']} rowHeaders={true} navigableHeaders={true} dropdownMenu={true} filters={true} contextMenu={true} emptyDataState={{ message: (source) => { switch (source) { case 'filters': return { title: 'No results found', description: 'Your current filters are hiding all results. Try adjusting your search criteria.', buttons: [ { text: 'Clear Filters', type: 'secondary', callback: () => { const filtersPlugin = hotTableRef.current?.hotInstance.getPlugin('filters');
if (filtersPlugin) { filtersPlugin.clearConditions(); filtersPlugin.filter(); } }, }, ], }; default: return { title: 'No data available', description: "There's nothing to display yet. Add some data to get started.", buttons: [ { text: 'Add Sample Data', type: 'primary', callback: () => { hotTableRef.current?.hotInstance.loadData([ ['John', 'Doe', 'john@example.com'], ['Jane', 'Smith', 'jane@example.com'], ['Bob', 'Johnson', 'bob@example.com'], ['Alice', 'Johnson', 'alice@example.com'], ]); }, }, ], }; } }, }} licenseKey="non-commercial-and-evaluation" /> );};
export default ExampleComponent;import React, { useRef } from 'react';import { HotTable } from '@handsontable/react-wrapper';import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modulesregisterAllModules();
const ExampleComponent = () => { const hotTableRef = useRef(null);
return ( <HotTable ref={hotTableRef} data={[]} // Empty data to trigger empty state height="auto" colHeaders={['First Name', 'Last Name', 'Email']} rowHeaders={true} navigableHeaders={true} dropdownMenu={true} filters={true} contextMenu={true} emptyDataState={{ message: (source) => { switch (source) { case 'filters': return { title: 'No results found', description: 'Your current filters are hiding all results. Try adjusting your search criteria.', buttons: [ { text: 'Clear Filters', type: 'secondary', callback: () => { const filtersPlugin = hotTableRef.current?.hotInstance.getPlugin('filters');
if (filtersPlugin) { filtersPlugin.clearConditions(); filtersPlugin.filter(); } }, }, ], }; default: return { title: 'No data available', description: "There's nothing to display yet. Add some data to get started.", buttons: [ { text: 'Add Sample Data', type: 'primary', callback: () => { hotTableRef.current?.hotInstance.loadData([ ['John', 'Doe', 'john@example.com'], ['Jane', 'Smith', 'jane@example.com'], ['Bob', 'Johnson', 'bob@example.com'], ['Alice', 'Johnson', 'alice@example.com'], ]); }, }, ], }; } }, }} licenseKey="non-commercial-and-evaluation" /> );};
export default ExampleComponent;Localize empty data state
Translate default empty data state labels using the global translations mechanism. The empty data state plugin introduces the following keys to the language dictionary that you can use to translate the empty state UI:
| Key | Default Value |
|---|---|
EMPTY_DATA_STATE_TITLE | 'No data available' |
EMPTY_DATA_STATE_DESCRIPTION | 'There's nothing to display yet.' |
EMPTY_DATA_STATE_TITLE_FILTERS | 'No results found' |
EMPTY_DATA_STATE_DESCRIPTION_FILTERS | 'It looks like your current filters are hiding all results.' |
EMPTY_DATA_STATE_BUTTONS_FILTERS_RESET | 'Reset filters' |
To learn more about the translation mechanism, see the Languages guide.
Result
After enabling the plugin, the grid displays a centered overlay message whenever there is no data to show. When the Filters plugin is also enabled, the overlay automatically switches to a filter-specific message and shows a “Reset filters” button when all rows are hidden by active filter conditions.