Column Types in Apex Grid

Each column declares a type that controls three things: how the cell renders, which editor it uses in inline-editing mode, and which filter operands the column offers. As of apex-grid 3.4.0 there are 13 types. string is the default when type is omitted.

One thing type does not control is sort order. Sorting compares the stored values, so it follows the shape of your data rather than the column's type. See What type affects below.

The 13 types

TypeDisplayEditorConfigure with
string (default)Plain textSingle-line text input
numberPlain text, tabular figuresNumeric input
booleanCheckbox, toggled in placeToggles from the cell, no edit mode
selectThe matched option's labelDropdown of optionsoptions
ratingStar barStar bar with keyboard supportmax (default 5)
dateLocale-formatted dateNative date pickerformat (default 'medium')
imageInline <img>, lazy-loadedText input for the URLshape, alt
currencyIntl.NumberFormat money valueNumeric inputcurrency (default 'USD'), locale
avatarThe first letter in a tinted circleDisplay only
badgeA pillDisplay onlybadgeVariant
progressA bar with a percentage labelDisplay onlymax (default 100)
sparklineAn inline trend chartDisplay onlyshowDelta (default true)
statusA pill with a leading dotDisplay onlystatusVariant

The five marked display only have no dedicated editor. If you set editable: true on one, the cell falls back to the standard text editor, so the reader sees a pill and the editor sees the raw value.

number renders the value as-is. It gets tabular figures for vertical alignment but no thousands separators, so 492000 displays as 492000. Use currency for money, or a cellTemplate for any other number formatting.

Since v3.3, number and currency columns left-align by default, matching the common data-grid default. If your app relied on right-aligned numbers, set the alignment yourself through the column's cell template or styling.

Declaring a column type

{
  key: 'createdAt',
  type: 'date',
}

For types that read a configuration option:

{
  key: 'priority',
  type: 'select',
  options: [
    { value: 'low',    label: 'Low'    },
    { value: 'medium', label: 'Medium' },
    { value: 'high',   label: 'High'   },
  ],
}
{
  key: 'score',
  type: 'rating',
  max: 5,
}

A bare value list also works for select, in which case each option's label is String(value):

{ key: 'priority', type: 'select', options: ['low', 'medium', 'high'] }

Value types

These seven carry the data's own meaning, and six of them are editable.

boolean

Renders a checkbox. When the column is editable, the checkbox commits directly from the cell, so a boolean column has no separate edit mode. The test is strict: only true renders as checked, so 1 and 'true' show as unchecked.

date

Accepts Date instances, ISO or otherwise parseable strings, and millisecond timestamps, and commits back in the same shape. format selects an Intl.DateTimeFormatOptions.dateStyle preset: 'short', 'medium' (default), 'long' or 'full'.

YYYY-MM-DD strings are read as floating dates (local midnight), so they render on the same calendar day in every timezone. A full ISO timestamp with a Z is a real instant and does shift with the viewer's timezone.

image

Renders the value as an <img src> with loading="lazy". shape is 'square' (default) or 'circle'. alt sets the alt text and defaults to the column key.

{
  key: 'photo',
  type: 'image',
  shape: 'circle',
  alt: 'Team member photo',
}

currency

Formats a number through Intl.NumberFormat with style: 'currency'. currency takes an ISO 4217 code and defaults to 'USD'; locale takes a BCP 47 tag and defaults to the runtime locale.

{
  key: 'revenue',
  type: 'currency',
  currency: 'EUR',
  locale: 'de-DE',
}

A value that is not a finite number renders as an empty cell. Clearing the editor commits null rather than NaN (changed in 3.4.0).

Presentation renderers

These five render a primitive value as a visual. They are display only, and they do not change the stored value.

avatar

Renders the first letter of the value, uppercased, in a tinted circle. The hue derives from the value itself, so the same name is always the same color. The full value stays available to assistive technology through role="img" and an aria-label.

badge

Renders the value as a pill. badgeVariant is 'gold', 'brand', 'neutral' (default) or 'muted', and accepts a callback for per-value colouring:

{
  key: 'plan',
  type: 'badge',
  badgeVariant: (value) => (value === 'Enterprise' ? 'gold' : 'neutral'),
}

progress

Renders a number as a bar. max is the value that fills the bar completely and defaults to 100. The fill colour tiers at 80% and 65% of max.

The trailing label is the percentage, not the raw value: with the default max of 100, a value of 72 reads 72, but with max: 5, a value of 4 also reads 80. Values outside the range are clamped rather than overflowing.

sparkline

Renders a number[] as an inline trend chart, with a trailing percentage-change label that showDelta: false removes.

{
  key: 'trend',
  type: 'sparkline',
  showDelta: true,
}
{ account: 'Northwind', trend: [12, 15, 14, 19, 23] }

It needs at least two finite numbers to draw anything; a shorter array, a single value or a non-array renders an empty cell. The delta compares the last value to the first, so it describes the endpoints rather than the shape between them.

status

Renders a pill with a leading dot. statusVariant is 'active', 'trial' or 'churn', and accepts a callback.

When you omit it, the variant is inferred from the value text, which is convenient until it surprises you:

MatchesVariant
churn, cancel, expired, inactive, lost, risk, fail, off, overduechurn
trial, trialing, pending, new, invited, watch, pausedtrial
anything else, including an unrecognised valueactive

Matching is case-insensitive and looks anywhere in the value, and the churn set is tested first, so "trial expired" resolves to churn. Because it matches substrings, a value can be caught by a word it merely contains: "Renewed" resolves to trial on the new in the middle of it, and "kickoff" resolves to churn on off. Set statusVariant explicitly whenever the values are not plain English states:

{
  key: 'health',
  type: 'status',
  statusVariant: (value) => (value === 'A' ? 'active' : 'churn'),
}

What type affects

type drives display, the editor and the filter operands. It does not drive sorting.

Driven byNotes
DisplaytypeA cellTemplate on the column takes precedence over the type's renderer.
EditortypeThe five presentation renderers fall back to the text editor.
Filter operandstypeOnly number and boolean differ from the string set.
Sort orderthe stored valuetype is not consulted.

Sorting follows your data, not the type

Sorting compares the raw stored values: two strings compare with localeCompare, anything else with < and >. So formatting a column with a cellTemplate never changes its sort order, and neither does its type.

What does change sort order is storing numbers as strings. ['9000', '99000', '100000', '492000'] sorts to 100000, 492000, 9000, 99000, because those are strings. The same values as numbers sort correctly. Fix the data, or supply a custom comparer on the column.

Only number and boolean change the filter operands

Every other type, currency, rating, progress and date included, offers the string operand set:

TypeOperands
numberequals, doesNotEqual, greaterThan, lessThan, greaterThanOrEqual, lessThanOrEqual, empty, notEmpty
booleanall, true, false, empty, notEmpty
everything elsecontains, doesNotContain, startsWith, endsWith, equals, doesNotEqual, empty, notEmpty

This matters most for money. A type: 'currency' column cannot be filtered with "greater than 100000", because it offers string operands. If a numeric filter matters more than the automatic money formatting, use type: 'number' and format the display with a cellTemplate:

{
  key: 'revenue',
  headerText: 'Revenue',
  type: 'number',                                   // numeric filter operands
  cellTemplate: ({ value }) => `$${(value / 1000).toFixed(0)}k`,
}

Note also that the quick filter matches against the stored value, not the rendered text. A revenue cell displaying $492,000 will not match a search for "492,000", because the stored value is 492000.

Customizing cell display

The 13 types cover the common cases. For anything else, use the per-column cellTemplate: it receives the cell context and returns a Lit template rendered in the cell. It takes precedence over the type's own renderer, so you can keep a type for its filter operands and editor while rendering something entirely different.