Skip to content

Styling Cells

Community

Cell styling paints a single cell — a red figure for a loss, a teal wash on a standout number, a monospace column of IDs. It comes in two layers:

  • Per column (Community). Set cellClass and cellStyle right on a ColumnDef. Each is a static value applied to every cell in that column, or a callback evaluated per cell against its own value and row. No license needed.
  • Grid-wide (Enterprise). For value-driven rules that span all columns — classic conditional formatting, heatmaps, cross-column highlights — use cellStyling() and hand it to the grid as cellStyleProvider. It sees every cell, so you describe the rules once instead of wiring each column.

Both feed the same renderer, so they compose: a column’s own styling and any grid-wide provider are merged onto the cell (the provider wins on conflicts).

cellClass hooks into your stylesheet, so the styling lives in CSS and composes with hover, selection and your design tokens. cellStyle is inline — self-contained and highest precedence, so it always wins.

Declare styling on the column itself:

const grid = new Zengrid(el, {
columns: [
{
field: 'revenue',
header: 'Revenue',
// Static applies to every cell; a callback is evaluated per cell.
cellStyle: ({ value }) =>
(value as number) > 200000 ? { color: '#2d9c8f', fontWeight: 700 } : null,
cellClass: ({ value }) => ((value as number) < 60000 ? 'cool' : null),
},
],
});

Each callback receives { row, col, field, value, data } — the cell’s value plus its row index and record. Return the class/style, or null/undefined for none. Style keys may be camelCase (backgroundColor) or kebab-case (background-color); custom properties (--accent) pass through untouched. cellClass accepts a single class, an array, or a space-separated string — they’re merged and de-duplicated.

cellStyle takes a static object (MRR) or a per-cell callback (Revenue). Change the 200000 threshold or the colours and the grid re-renders.

Grid-wide conditional formatting (Enterprise)

Section titled “Grid-wide conditional formatting (Enterprise)”
Enterprise

When a rule spans columns — any negative number red, a revenue heatmap, dim churned rows’ names — reach for cellStyling(). Build it once and pass it as cellStyleProvider; it’s evaluated for every visible cell:

import { cellStyling } from '@zengrid/enterprise/grid';
const grid = new Zengrid(el, {
cellStyleProvider: cellStyling({
// Class ↔ predicate: the class is added whenever the predicate matches.
cellClassRules: {
negative: ({ value }) => typeof value === 'number' && value < 0,
},
// Or a grid-wide cellStyle callback, evaluated per cell.
cellStyle: ({ value }) =>
typeof value === 'number' && value > 1000 ? { fontWeight: 700 } : null,
}),
});

cellStyling() is license-gated (asserts the cell-styling feature). Its callbacks receive the same { row, col, field, value, data } as the per-column ones, but for every cell — use col/field to scope a rule to a column and data to read the whole row.

Each class ↔ predicate rule adds its class to matching cells. Scroll to spot the hot/cool Revenue and low MRR. Edit the thresholds or the injected CSS.

interface CellStyleParams {
row: number; // display-order row index
col: number; // display-order column index
field: string; // the column's field
value: unknown; // the cell's displayed value
data: unknown; // the full row record
}
// On a ColumnDef (Community):
cellClass?: CellClassValue | ((p: CellStyleParams) => CellClassValue);
cellStyle?: CellStyleValue | ((p: CellStyleParams) => CellStyleValue);
// cellStyling() options (Enterprise):
interface CellStylingOptions {
cellClass?: CellClassValue | ((p: CellStyleParams) => CellClassValue);
cellStyle?: CellStyleValue | ((p: CellStyleParams) => CellStyleValue);
cellClassRules?: Record<string, (p: CellStyleParams) => boolean>;
}
type CellClassValue = string | string[] | null | undefined;
type CellStyleValue = Record<string, string | number> | null | undefined;
  • Styling lands on the cell. Classes/styles are applied to the single cell element, so target .zg-cell.your-class in CSS.
  • Precedence. Inline cellStyle wins over everything, including the theme; the grid-wide provider is applied after (and so wins over) a column’s own styling on conflicting keys. cellClass composes by CSS specificity.
  • Both layers compose. A column’s cellClass/cellStyle and a cellStyleProvider are merged onto the cell — use the column for column-local rules and the provider for anything grid-wide.
  • Keep callbacks cheap and pure. They run during rendering for each visible cell. Read a flag off the value/row here; do heavy work up front.