developer guide
JavaScript Data Grid Cell Renderers
A renderer decides what a cell looks like without changing what it holds, so sorting, filtering and totals keep working on the real value. Set cell.render on a column for your own markup, or take one of the built-in looks such as trafficLights, arrows or ratings.
Developer guide › Columns and cell rendering › JavaScript Data Grid Cell Renderers
Cells and renderers
By default a cell writes text. When you want more, cell takes a decoration, a
named renderer, a template or a component.
Decorations, the common cases, without writing a renderer
cell: { decoration: 'pill' } // a status chip
cell: { decoration: 'bar', min: 0, max: 1 } // an inline bar
cell: { decoration: 'heat', ramp: 'redGreen' } // a heat fill
cell: { decoration: 'dot' } // a leading dot
Variants: mapping a value to a semantic colour
cell: {
decoration: 'pill',
variant: { when: [
{ op: 'eq', value: 'Escalated', use: 'danger' },
{ op: 'eq', value: 'Pending', use: 'warning' },
], default: 'success' },
}
Variants are semantic tokens rather than colours: danger, not
#c22b2b. The theme decides what danger looks like, and it looks the same in the
status pill, the filter chip and the validation message. Changing the palette is one custom
property, not a search for hex codes.
Icon sets: a threshold glyph per value band
// A built-in set - traffic lights, arrows, rating marks - driven by value.
cell: { decoration: { type: 'icon', iconSet: 'arrows' } }
// Or your own bands. The highest `min` a value clears wins; a band with no
// `min` is the catch-all. `label` is what a screen reader announces.
cell: { decoration: { type: 'icon', bands: [
{ min: 0.9, icon: 'success', label: 'on target', variant: 'success' },
{ min: 0.5, icon: 'warning', label: 'at risk', variant: 'warning' },
{ icon: 'danger', label: 'off track', variant: 'danger' },
] } }
An icon set is a restatement of the value, not a replacement for it: the value still
renders beside the glyph, and the band's label is set as the glyph's
aria-label, so a screen-reader user hears "on target 92%" rather than a bare
number with the status lost. The glyphs are the grid's own inline sprites (), so an icon
set adds no dependency and makes no request. Built-in sets: trafficLights,
arrows, trafficArrows, ratings.
Turning a decoration on at runtime
// Set, change or clear a column's decoration after the grid is built.
grid.columns.decorate('score', { type: 'bar', min: 0, max: 100 });
grid.columns.decorate('trend', { type: 'icon', iconSet: 'arrows' });
grid.columns.decorate('score', null); // back to plain text
A decoration is presentation, so columns.decorate is a live setter like
grid.set('theme', …): it is not on the undo timeline and does
not travel in a saved view. For conditional styling that a user edits and a view
remembers, reach for grid.formatting below, which holds colour and weight rules
as durable state.
Your own renderer
components: {
sparkline: {
render(el, p) { el.appendChild(draw(p.value)); },
refresh(el, p) { update(el, p.value); return true; },
release(el) { el.textContent = ''; },
},
},
columns: [{ field: 'history', cell: { render: 'sparkline' } }],
refresh returning true is the contract that makes recycling work:
it means "I updated in place, keep this element". Return false and the grid
rebuilds the cell. A renderer that only implements render still works, it is
just rebuilt on every reuse.
Templates, and what allowUnsafeTemplates permits
cell: { template: '<span class="sku">{{ value }}</span>' } // escaped
// Raw interpolation needs the grid-level opt-in:
allowUnsafeTemplates: true,
cell: { template: '<span>{{{ value }}}</span>' }
The flag permits markup, not code. Without it, {{ }} escapes
and a {{{ }}} segment is refused outright. With it, an interpolated value may
carry presentational markup: <b>, <a href>, a
<span>, and everything executable is still stripped from it:
<script>, <iframe>, <style> and the
other code-bearing tags, every on* handler attribute, and
javascript: or data: URLs including entity-encoded spellings of
them. The same rules apply to a string returned from cell.render, which is the
same gate.
Why the value is treated differently from the template. You wrote the template and can audit it; the value is row data and usually arrives from somewhere you cannot. A template is refused at compile time for a dangerous tag, but that refusal says nothing about what a value interpolated into it might contain.
It is a narrow allowance, not a sanitiser. It exists so a grid cell can
show emphasis and a link. To render arbitrary third-party HTML, sanitise it yourself and
return an element from cell.render.