Conditional Styling
Style rows, cells and headers from their data, and format numbers with colour scales, data bars and icon sets.
styleRules applies CSS classes conditionally, based on row data or column identity — no per-render inline style
computation, and no separate compiler step. Pass an array of rule objects to <Grid styleRules={...} />; the
grid compiles and re-applies them whenever the array reference changes.
import { useMemo } from 'react';
import { Grid, type ColumnDef, type StyleRule } from '@witbybit/wit-grid-react';
interface ProductRow {
id: string;
name: string;
price: number;
status: 'Active' | 'Inactive';
}
export function ProductGrid({ rows, columns }: { rows: ProductRow[]; columns: ColumnDef<ProductRow>[] }) {
const styleRules = useMemo<StyleRule<ProductRow>[]>(
() => [
{
kind: 'row',
when: (row) => row.status === 'Inactive',
rowClass: 'row-inactive',
},
{
kind: 'cell',
field: 'price',
when: (row) => row.price > 500,
cellClass: 'cell-high-value',
},
{
kind: 'headerCell',
field: 'price',
when: () => true,
headerCellClass: 'header-emphasis',
},
],
[]
);
return <Grid rows={rows} columns={columns} getRowId={(row) => row.id} styleRules={styleRules} />;
}Rule kinds
kind | Targets | Class field | when receives |
|---|---|---|---|
row | A data row | rowClass | (row, params) |
groupRow | A group row | rowClass | (visualRow) — optional, defaults true |
detailRow | A master-detail row | rowClass | (visualRow) — optional, defaults true |
cell | A cell, optionally scoped to field | cellClass | (row, col, params) |
headerCell | A header cell, optionally scoped to field | headerCellClass | (col) |
field on cell and headerCell rules is optional — omit it to evaluate the rule against every column.
Rules are additive: a row or cell can match several rules at once, and all matching classes are applied together.
Prefer styleRules for conditional decoration
Use styleRules for data-driven decoration (status colors, thresholds, validation highlights). Use the theming API for
token-level, grid-wide visual changes (surface colors, borders, fonts).
Conditional formatting
Three more rule kinds format a cell by where its number sits in its column: a colour scale tints it, a data bar draws a bar behind it, an icon set puts an arrow, dot or signal before it.
import type { StyleRule } from '@witbybit/wit-grid-react';
type Deal = { id: string; amount: number; margin: number; growth: number; latency: number };
const styleRules: StyleRule<Deal>[] = [
{ kind: 'dataBar', field: 'amount' },
{ kind: 'colorScale', field: 'margin' }, // red → amber → green
{ kind: 'iconSet', field: 'growth', icons: 'arrows' },
{ kind: 'iconSet', field: 'latency', icons: 'dots', reverse: true }, // low is good
];kind | Options |
|---|---|
colorScale | colors (two or three CSS colours, low → high), mid, opacity (default 0.24) |
dataBar | color (default the theme accent), negativeColor (default red) |
iconSet | icons: 'arrows' · 'dots' · 'signal'; thresholds (default [1/3, 2/3]); reverse |
Every kind takes field and optional fixed min / max.
- The range is the column's, over the rows currently displayed: filter the grid and the scale re-fits. Give
min/maxto keep cells comparable across filters. - Live data moves it. When a visible cell shows a new value the range is recomputed (at most every 200 ms) and the visible cells repaint. Infinite and server row models keep a running range of the values seen.
- Data bars grow from zero, so gains and losses read both ways.
- Rules stack on one field: the icon draws over the bar, the bar over the tint.
- Colours are mixed by the browser, so palette names, any CSS colour and theme variables all work.
- Cheap: painted as background layers on the cell itself (no extra elements), written only when a cell's look changes.
- Excel keeps them:
exportExcelwrites them as native conditional formats.
Row animations
When a sort, a live value change under the current sort, a group expanding or a detail row opening moves rows, they animate to their new place. Scrolling never animates, and users who prefer reduced motion get instant changes. Animations use only transform and opacity, so they run on the GPU and cost nothing per frame.
rowAnimation chooses how they move. Change it any time; it applies from the next change.
<Grid rows={rows} columns={columns} getRowId={(row) => row.id} rowAnimation={{ style: 'slide', easing: 'spring', duration: 520, stagger: 18 }} />| Option | Default | Effect |
|---|---|---|
style | 'slide' | 'slide': rows glide from their old place. 'fade': moved rows fade in at their new place. 'none'. |
easing | 'smooth' | 'smooth', 'snappy' (quick start, soft stop), 'spring' (settles with a small overshoot), or any CSS easing. |
duration | 280 | Milliseconds per row. |
stagger | 0 | Milliseconds between rows, top to bottom, so they cascade into place. The whole cascade is capped at 240 ms. |
on | all | { sort, liveReorder, expand, detail }: turn individual kinds of change off. |
Outside React, pass rowAnimation when creating the grid and change it with
api.setOptions({ rowAnimation }). The Realtime Dashboard has a Motion picker to compare styles
on a live-sorted feed.
Last updated on