Lattice Grid Buy a licence

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 guideColumns 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.