Skip to content

Formatting cells

Change the appearance of cells, using custom CSS classes, inline styles, or custom cell borders.

Overview

Handsontable renders an HTML table, so you can style existing tr and td elements or attach your own classes.

Choose an approach based on your goal:

  • Use className when you want reusable static styles.
  • Use renderer when you need to apply inline styles to specific cells at render time.
  • Use customBorders when you need custom border widths, colors, or styles for selected ranges.

Prerequisites

  • A Handsontable instance with data loaded.
  • A stylesheet, if you format cells through custom CSS classes.

Apply custom CSS class styles

In this example, you add a custom class custom-cell to the cell in the top-left corner and a custom-table CSS class that highlights the table headers.

To add a CSS class to a cell, column, or row, use the className option. Set it in the grid configuration, in a columns entry, or per cell through the cells callback. The class you provide is added to the cell’s <td> element, where your CSS rules can target it.

TypeScript
/* file: app.component.ts */
import { Component, ViewEncapsulation } from '@angular/core';
import { GridSettings, HotTableModule } from '@handsontable/angular-wrapper';
@Component({
selector: 'example1-formatting-cells',
standalone: true,
imports: [HotTableModule],
template: ` <div>
<hot-table [data]="data" [settings]="gridSettings"></hot-table>
</div>`,
styles: `hot-table td.custom-cell {
color: #fff !important;
background-color: #254ac6 !important;
}
hot-table .custom-table thead th:nth-child(even),
hot-table .custom-table tbody tr:nth-child(odd) th {
color: #fff !important;
background-color: #254ac6 !important;
}
`,
encapsulation: ViewEncapsulation.None,
})
export class AppComponent {
readonly data = [
['SKU-4821', 'Laptop Pro 15', 'Electronics', 149900, 42],
['SKU-0093', 'Wireless Mouse', 'Peripherals', 2999, 218],
['SKU-7712', 'USB-C Hub 7-port', 'Peripherals', 5499, 0],
['SKU-3305', 'Mech. Keyboard', 'Peripherals', 8999, 67],
['SKU-9140', '4K Monitor 27"', 'Electronics', 34999, 15],
];
readonly gridSettings: GridSettings = {
rowHeaders: true,
colHeaders: ['SKU', 'Product', 'Category', 'Price ($)', 'Stock'],
stretchH: 'all',
className: 'custom-table',
cell: [
{
row: 0,
col: 0,
className: 'custom-cell',
},
],
height: 'auto',
autoWrapRow: true,
autoWrapCol: true
};
}
/* 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';
// register Handsontable's modules
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-formatting-cells></example1-formatting-cells>
</div>

Apply inline styles

Apply inline styles directly to a cell’s DOM element through the style property. Use the renderer option to run this logic on each render.

TypeScript
/* file: app.component.ts */
import { Component } from '@angular/core';
import { GridSettings, HotTableModule } from '@handsontable/angular-wrapper';
import Handsontable from 'handsontable/base';
import { registerRenderer, textRenderer } from 'handsontable/renderers';
registerRenderer('customStylesRenderer', (hotInstance: Handsontable, TD: HTMLTableCellElement, row: number, col: number, prop: string | number, value: unknown, cellProperties: Handsontable.CellProperties) => {
textRenderer(hotInstance, TD, row, col, prop, value, cellProperties);
TD.style.fontWeight = 'bold';
TD.style.color = 'green';
TD.style.background = '#d7f1e1';
});
@Component({
selector: 'example2-formatting-cells',
standalone: true,
imports: [HotTableModule],
template: ` <div>
<hot-table [data]="data" [settings]="gridSettings"></hot-table>
</div>`,
})
export class AppComponent {
readonly data = [
['SKU-4821', 'Laptop Pro 15', 'Electronics', 149900, 42],
['SKU-0093', 'Wireless Mouse', 'Peripherals', 2999, 218],
['SKU-7712', 'USB-C Hub 7-port', 'Peripherals', 5499, 0],
['SKU-3305', 'Mech. Keyboard', 'Peripherals', 8999, 67],
['SKU-9140', '4K Monitor 27"', 'Electronics', 34999, 15],
];
readonly gridSettings: GridSettings = {
rowHeaders: true,
colHeaders: ['SKU', 'Product', 'Category', 'Price ($)', 'Stock'],
stretchH: 'all',
cell: [
{
row: 0,
col: 0,
renderer: 'customStylesRenderer',
},
],
height: 'auto',
autoWrapRow: true,
autoWrapCol: true
};
}
/* 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';
// register Handsontable's modules
registerAllModules();
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
{
provide: HOT_GLOBAL_CONFIG,
useValue: { license: NON_COMMERCIAL_LICENSE } as HotGlobalConfig,
},
],
};
/* end-file */
HTML
<div>
<example2-formatting-cells></example2-formatting-cells>
</div>

Custom cell borders

To enable custom borders, set customBorders. You can set it to true or pass an array with predefined border configuration. For all settings and methods, see the API reference.

In API property names, start and end refer to the starting and ending edges of the layout direction.

You can customize the border style using the style property in the border configuration. The available options are:

  • 'solid' (default) - A solid line border
  • 'dashed' - A dashed line border
  • 'dotted' - A dotted line border

The style property can be set for any border edge (top, bottom, start, end). When not specified, it defaults to 'solid'.

The following example shows different border styles on selected cell ranges.

TypeScript
/* file: app.component.ts */
import { Component } from '@angular/core';
import { GridSettings, HotTableModule } from '@handsontable/angular-wrapper';
import Handsontable from 'handsontable/base';
import { registerRenderer, textRenderer } from 'handsontable/renderers';
registerRenderer('customStylesRenderer', (hotInstance: Handsontable, TD: HTMLTableCellElement, row: number, col: number, prop: string | number, value: unknown, cellProperties: Handsontable.CellProperties) => {
textRenderer(hotInstance, TD, row, col, prop, value, cellProperties);
TD.style.fontWeight = 'bold';
TD.style.color = 'green';
TD.style.background = '#d7f1e1';
});
@Component({
selector: 'example3-formatting-cells',
standalone: true,
imports: [HotTableModule],
template: ` <div>
<hot-table [data]="data" [settings]="gridSettings"></hot-table>
</div>`,
})
export class AppComponent {
readonly data = [
['SKU-4821', 'Laptop Pro 15', 'Electronics', 149900, 42],
['SKU-0093', 'Wireless Mouse', 'Peripherals', 2999, 218],
['SKU-7712', 'USB-C Hub 7-port', 'Peripherals', 5499, 0],
['SKU-3305', 'Mech. Keyboard', 'Peripherals', 8999, 67],
['SKU-9140', '4K Monitor 27"', 'Electronics', 34999, 15],
];
readonly gridSettings: GridSettings = {
rowHeaders: true,
colHeaders: ['SKU', 'Product', 'Category', 'Price ($)', 'Stock'],
stretchH: 'all',
customBorders: [
{
range: {
from: {
row: 1,
col: 1,
},
to: {
row: 3,
col: 4,
},
},
top: {
width: 2,
color: '#5292F7',
style: 'dotted',
},
bottom: {
width: 2,
color: 'red',
},
start: {
width: 2,
color: 'orange',
style: 'dashed',
},
end: {
width: 2,
color: 'magenta',
},
},
{
row: 2,
col: 2,
start: {
width: 2,
color: 'red',
},
end: {
width: 1,
color: 'green',
},
},
],
height: 'auto',
autoWrapRow: true,
autoWrapCol: true
};
}
/* 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';
// register Handsontable's modules
registerAllModules();
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
{
provide: HOT_GLOBAL_CONFIG,
useValue: { license: NON_COMMERCIAL_LICENSE } as HotGlobalConfig,
},
],
};
/* end-file */
HTML
<div>
<example3-formatting-cells></example3-formatting-cells>
</div>

Result

The grid renders your configured classes, inline styles, and border definitions. Formatting stays consistent after each render.

Related guides

Configuration options

Plugins