Skip to content

In this tutorial, you will add a search input outside Handsontable that highlights matching cells as you type. You will learn how to use the Search plugin’s query() method and hot.render() to apply real-time cell highlights from an external control.

TypeScript
/* file: app.component.ts */
import { Component, ViewChild } from '@angular/core';
import { GridSettings, HotTableComponent, HotTableModule } from '@handsontable/angular-wrapper';
import { RowObject } from 'handsontable/common';
const data = [
['Alice Johnson', 'Engineering', 'Berlin', 'alice.johnson@example.com'],
['Noah Smith', 'Design', 'Warsaw', 'noah.smith@example.com'],
['Mia Garcia', 'Marketing', 'New York', 'mia.garcia@example.com'],
['Liam Brown', 'Engineering', 'Toronto', 'liam.brown@example.com'],
['Emma Davis', 'Sales', 'London', 'emma.davis@example.com'],
['Oliver Miller', 'Support', 'Madrid', 'oliver.miller@example.com'],
];
@Component({
standalone: true,
imports: [HotTableModule],
selector: 'example1-external-search-box',
template: `
<div class="example-controls-container">
<div class="controls">
<label for="external-search-input">Search rows</label>
<input
id="external-search-input"
type="search"
placeholder="Type to highlight matching cells..."
style="min-width: 20rem"
(input)="onSearch($event)"
/>
</div>
</div>
<hot-table [data]="data" [settings]="gridSettings"></hot-table>
`,
})
export class AppComponent {
@ViewChild(HotTableComponent, { static: false }) readonly hotTable!: HotTableComponent;
readonly data = data;
readonly gridSettings: GridSettings = {
rowHeaders: true,
colHeaders: ['Name', 'Team', 'Location', 'Email'],
height: 'auto',
width: '100%',
autoWrapRow: true,
autoWrapCol: true,
search: true,
};
private debounceTimer: ReturnType<typeof setTimeout> | undefined;
onSearch(event: Event): void {
const value = (event.target as HTMLInputElement).value;
clearTimeout(this.debounceTimer);
this.debounceTimer = setTimeout(() => {
const hot = this.hotTable.hotInstance;
if (!hot) {
return;
}
hot.getPlugin('search').query(value);
hot.render();
}, 120);
}
}
/* end-file */
/* file: app.config.ts */
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';
registerAllModules();
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
{
provide: HOT_GLOBAL_CONFIG,
useValue: { license: NON_COMMERCIAL_LICENSE } as HotGlobalConfig,
},
],
};
/* end-file */
HTML
<div><example1-external-search-box></example1-external-search-box></div>
  1. Enable the Search plugin

    Set search: true in grid settings:

    const hot = new Handsontable(container, {
    data,
    search: true,
    licenseKey: 'non-commercial-and-evaluation',
    });

    This enables the plugin and the default htSearchResult highlight class.

  2. Add an external input

    Render a text input above the grid container:

    const controls = document.createElement('div');
    const searchInput = document.createElement('input');
    searchInput.type = 'search';
    searchInput.placeholder = 'Search in table...';
    controls.appendChild(searchInput);
    container.parentElement?.insertBefore(controls, container);

    The input lives outside Handsontable, so you can style and place it like any other app control.

  3. Bind the input to query()

    Listen to input events, query the plugin, and re-render:

    const searchPlugin = hot.getPlugin('search');
    searchInput.addEventListener('input', () => {
    searchPlugin.query(searchInput.value);
    hot.render();
    });

    query() updates each cell’s isSearchResult metadata. hot.render() applies the updated highlight state.

Step 4 (optional): Debounce for large datasets

If you search very large tables, debounce the input callback to reduce render frequency:

function debounce<T extends (...args: any[]) => void>(callback: T, wait = 120) {
let timeoutId: ReturnType<typeof setTimeout> | undefined;
return (...args: Parameters<T>) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => callback(...args), wait);
};
}

Use this wrapper around your search handler when needed.

What you learned

  • How to enable the Search plugin with search: true in Handsontable settings.
  • How to place a search input outside the grid and call hot.getPlugin('search').query(value) on every input event.
  • Why you must call hot.render() after query() to apply the updated isSearchResult metadata to cells.
  • How to add debouncing to limit render frequency when searching large datasets.

Next steps