Skip to content

Styling Rows

Enterprise

Row styling paints whole rows — a subtle zebra stripe, a red tint on the overdue row, a bold total line. You describe the styling declaratively with two options, each of which can be a static value applied to every row or a callback evaluated per row against that row’s data:

  • rowClass — CSS class names added to the row. They hook into your stylesheet, so the styling lives in CSS and composes with hover, selection and your design tokens.
  • rowStyle — inline CSS applied directly to the row. Self-contained (no stylesheet needed) and highest precedence, so it always wins.

Because ZenGrid renders individual pooled cells rather than a <tr> per row (that is what keeps 100k rows smooth), there is no row element to style. The resolved classes and styles are instead stamped onto every cell of the row — the effect is identical, and it stays correct as rows recycle during scroll.

Row styling ships in @zengrid/enterprise. Build a provider with rowStyling() and hand it to the grid through the rowStyleProvider option:

import { rowStyling } from '@zengrid/enterprise/grid';
const grid = new Zengrid(el, {
rowCount,
rowStyleProvider: rowStyling({
// Static: applied to every row.
rowClass: 'zebra',
// Callback: evaluated per row. `data` is the row record; `row` is its index.
rowStyle: ({ row, data }) =>
data.status === 'overdue' ? { backgroundColor: '#3a1113' } : null,
}),
columns: [/* … */],
});

Each callback receives { row, data } — the row index and the row’s record — and returns the class/style for that row, or null/undefined for no styling. Style keys may be camelCase (backgroundColor) or kebab-case (background-color); custom properties (--accent) pass through untouched.

interface RowStyleParams {
/** Row index in display order. */
row: number;
/** The row's record. */
data: unknown;
}
type RowClassValue = string | string[] | null | undefined;
type RowStyleValue = Record<string, string | number> | null | undefined;
interface RowStylingOptions {
rowClass?: RowClassValue | ((params: RowStyleParams) => RowClassValue);
rowStyle?: RowStyleValue | ((params: RowStyleParams) => RowStyleValue);
}

rowClass accepts a single class, an array, or a space-separated string — they’re merged and de-duplicated. rowStyle values are stringified, so opacity: 0.6 and fontWeight: 600 both work.

Each tab styles rows a different way. Edit the callbacks — change the stripe colour, the threshold, or the class rules — and the grid re-renders.

rowStyle: ({ row }) => (…) keys off the row index. Change the stripe colour, or make the condition row % 3 to stripe every third row.

  • No <tr> — styling lands on cells. The classes/styles are applied to every cell of the row, so target .zg-cell.your-class in CSS. This is what lets row styling stay correct while rows recycle during virtualized scroll.
  • Precedence. Inline rowStyle wins over everything, including the theme. rowClass composes by CSS specificity, so .zg-cell.your-class beats the base .zg-cell rules but still layers under selection/active states.
  • Keep callbacks cheap and pure. They run during rendering for each visible row (memoized per render pass). Do the heavy work up front and read a flag off the row’s data here.
  • Static or callback, per option. rowClass and rowStyle are independent — use a static string for one and a callback for the other, or mix freely.