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
| Type | Display | Editor | Configure with |
|---|---|---|---|
string (default) | Plain text | Single-line text input | |
number | Plain text, tabular figures | Numeric input | |
boolean | Checkbox, toggled in place | Toggles from the cell, no edit mode | |
select | The matched option's label | Dropdown of options | options |
rating | Star bar | Star bar with keyboard support | max (default 5) |
date | Locale-formatted date | Native date picker | format (default 'medium') |
image | Inline <img>, lazy-loaded | Text input for the URL | shape, alt |
currency | Intl.NumberFormat money value | Numeric input | currency (default 'USD'), locale |
avatar | The first letter in a tinted circle | Display only | |
badge | A pill | Display only | badgeVariant |
progress | A bar with a percentage label | Display only | max (default 100) |
sparkline | An inline trend chart | Display only | showDelta (default true) |
status | A pill with a leading dot | Display only | statusVariant |
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:
| Matches | Variant |
|---|---|
churn, cancel, expired, inactive, lost, risk, fail, off, overdue | churn |
trial, trialing, pending, new, invited, watch, paused | trial |
| anything else, including an unrecognised value | active |
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 by | Notes | |
|---|---|---|
| Display | type | A cellTemplate on the column takes precedence over the type's renderer. |
| Editor | type | The five presentation renderers fall back to the text editor. |
| Filter operands | type | Only number and boolean differ from the string set. |
| Sort order | the stored value | type 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:
| Type | Operands |
|---|---|
number | equals, doesNotEqual, greaterThan, lessThan, greaterThanOrEqual, lessThanOrEqual, empty, notEmpty |
boolean | all, true, false, empty, notEmpty |
| everything else | contains, 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.