Skip to content

ColumnConfig

Full column configuration including custom renderers, editors, and grouping metadata.

Extends BaseColumnConfig with additional features for customizing how cells are displayed and edited.

const columns: ColumnConfig<Employee>[] = [
// Basic sortable column
{ field: 'id', header: 'ID', width: 60, sortable: true },
// Column with custom renderer
{
field: 'name',
header: 'Employee',
renderer: (ctx) => {
const div = document.createElement('div');
div.innerHTML = `<img src="${ctx.row.avatar}" /><span>${ctx.value}</span>`;
return div;
},
},
// Column with custom header
{
field: 'email',
headerLabelRenderer: (ctx) => `${ctx.value} 📧`,
},
// Editable column (requires EditingPlugin)
{
field: 'status',
editable: true,
editor: (ctx) => {
const select = document.createElement('select');
// ... editor implementation
return select;
},
},
// Hidden column (can be shown via VisibilityPlugin)
{ field: 'internalNotes', hidden: true },
];
PropertyTypeDescription
fieldkeyof TRow & stringUnique field key referencing property in row objects
header?stringVisible header label; defaults to capitalized field
type?ColumnTypeColumn data type.
width?string | numberColumn width in pixels; fixed size (no flexibility)
minWidth?numberMinimum column width in pixels (stretch mode only); when set, column uses minmax(minWidth, 1fr)
sortable?booleanWhether column can be sorted
resizable?booleanWhether column can be resized by user
sortComparator?(a: any, b: any, rowA: TRow, rowB: TRow) => numberOptional custom comparator for sorting (a,b) -> number
valueAccessor?(ctx: object) => anyCompute the cell’s value from the row. When defined, this is the single source of truth used by sorting, filtering, formatting, cell rendering, export, and clipboard — eliminating the need to duplicate value-extraction logic across sortComparator, filterValue, and per-renderer code.
options?object[] | () => object[]For select type - available options
format?(value: any, row: TRow) => stringFormats the raw cell value into a display string.
utility?booleanMarks this column as a system / utility column — a column that exists to support grid behaviour rather than to display user data.
meta?Record<string, unknown>Arbitrary extra metadata for application use.
renderer?ColumnViewRenderer<TRow, any>Optional custom cell renderer function. Alias for viewRenderer. Can return an HTMLElement, a Node, or an HTML string (which will be sanitized).
viewRenderer?ColumnViewRenderer<TRow, any>Optional custom view renderer used instead of default text rendering
externalView?objectExternal view spec (lets host app mount any framework component)
hidden?booleanWhether the column is initially hidden
lockVisible?booleanPrevent this column from being hidden programmatically
cellClass?(value: unknown, row: TRow, column: ColumnConfig<TRow>) => string | string[]Dynamic CSS class(es) for cells in this column. Called for each cell during rendering. Return class names to add to the cell element.
headerLabelRenderer?HeaderLabelRenderer<TRow>Custom header label renderer. Customize the label content while the grid handles sort icons, filter buttons, resize handles, and click interactions.
headerRenderer?HeaderRenderer<TRow>Custom header cell renderer. Complete control over the entire header cell. Resize handles are added automatically for resizable columns.
editable?boolean | (row: TRow) => booleanWhether the field is editable (enables editors). Requires EditingPlugin.
editor?ColumnEditorSpec<TRow, any>Optional custom editor factory or element tag name. Requires EditingPlugin.
multi?booleanFor select type - allow multi select. Requires EditingPlugin.
editorParams?EditorParamsConfiguration parameters for built-in editors. Shape depends on column type (NumberEditorParams, TextEditorParams, DateEditorParams, SelectEditorParams). Requires EditingPlugin.
nullable?booleanWhether this column allows null values. Requires EditingPlugin.
filterable?booleanWhether this column can be filtered (only applicable when FilteringPlugin is enabled).
filterParams?FilterParamsConfiguration for the filter UI (only applicable when FilteringPlugin is enabled). For number columns: { min, max, step } For date columns: { min, max } (ISO date strings) Falls back to editorParams if not set.
filterValue?(value: unknown, row: any) => unknownCustom value extractor for filtering. Use this when the cell value is a complex type (e.g., an array of objects) and the filter should operate on derived primitive values instead.
filterType?FilterTypeOverride the filter panel UI type independently of the column’s type.
group?string | objectColumn group assignment for the GroupingColumnsPlugin. Columns with the same group.id are rendered under a shared header.
pinned?PinnedPositionPin column to an edge of the grid.
lockPinning?booleanPrevent the user from unpinning or repinning this column via the header context menu. Programmatic changes are still allowed. Requires PinnedColumnsPlugin.
printHidden?booleanHide this column when printing (default: false). Use this to exclude interactive or less important columns from print output.
lockPosition?booleanPrevent this column from being reordered by the user. When true, the column cannot be dragged in the header row or rearranged via the visibility panel. Programmatic reordering (e.g. setColumnOrder()) is not affected.
checkboxColumn?booleanMarks this column as the selection checkbox column. Set automatically by SelectionPlugin on its synthesized utility column.
cellTooltip?string | false | (ctx: CellRenderContext<TRow, any>) => string | unknownCell tooltip configuration. Requires TooltipPlugin.
headerTooltip?string | false | (ctx: HeaderLabelContext<TRow>) => string | unknownHeader tooltip configuration. Requires TooltipPlugin.

Column data type.

Built-in types: 'string', 'number', 'date', 'boolean', 'select'

Custom types (e.g., 'currency', 'country') can have type-level defaults via gridConfig.typeDefaults or framework adapter registries.

Default: Inferred from first row data


Compute the cell’s value from the row. When defined, this is the single source of truth used by sorting, filtering, formatting, cell rendering, export, and clipboard — eliminating the need to duplicate value-extraction logic across sortComparator, filterValue, and per-renderer code.

Resolution precedence:

  • Sort: sortComparatorvalueAccessorrow[field]
  • Filter: filterValuevalueAccessorrow[field]
  • Render / format / export / copy: valueAccessorrow[field]

The accessor is the default value source — per-column escape hatches (sortComparator, filterValue) still take precedence when set.

Results are memoized per (row identity, column field) so accessors are free to be “slow but correct” (e.g. array.find(...)). Immutable row updates auto-invalidate; in-place mutations are invalidated by the grid’s edit / transaction paths.

Note: a valueAccessor without a paired valueSetter (planned API) implies the column is read-only — editors will not commit through it.

// Computed value from nested data
{
field: 'bolDate',
header: 'BL date',
valueAccessor: ({ row }) => {
if (isCargo(row)) {
return row.movements.find(m => m.operationType === 'LOAD')?.movementDate ?? null;
}
return row.movementDate ?? null;
},
filterType: 'date',
// No need for sortComparator or filterValue — they fall back to the accessor.
}

Formats the raw cell value into a display string.

Used both for cell rendering and the built-in filter panel:

  • In cells, the formatted value replaces the raw value as text content.
  • In the filter panel (set filter), checkbox labels show the formatted value instead of the raw value, and search matches against the formatted text.

The row parameter is available during cell rendering but is undefined when called from the filter panel (standalone value formatting). Avoid accessing row properties in format functions intended for filter display.

// Currency formatter — works in both cells and filter panel
{
field: 'price',
format: (value) => `$${Number(value).toFixed(2)}`,
}
// ID-to-name lookup — filter panel shows names, not IDs
{
field: 'departmentId',
format: (value) => departmentMap.get(value as string) ?? String(value),
}

Marks this column as a system / utility column — a column that exists to support grid behaviour rather than to display user data.

Built-in plugins set this on the columns they synthesize (selection checkbox, row-reorder drag handle, master-detail / tree / row-grouping expander). You can also set it on columns you author yourself when you want them to behave the same way — for example, a row-action menu column, a status indicator, or any developer-defined “system” column that should be visible in the grid only.

Setting utility: true excludes the column from:

  • Visibility panel — not listed in the show/hide UI
  • Column reorder — header drag-drop and visibility-panel drag treat it as locked (equivalent to lockPosition: true)
  • Print — hidden by PrintPlugin during print
  • Clipboard copy — skipped by ClipboardPlugin
  • Export (CSV / JSON / Excel) — skipped by ExportPlugin
  • Range / row selection — clicks land on the column but selection ignores it
  • Filter UI — no filter button rendered, no filter model entry

The column is still rendered in the grid and still receives cellRenderer / viewRenderer / headerRenderer callbacks — it is “hidden from the system, visible in the grid”.

Convention: name the field with a __-prefix (e.g. __actions) so it cannot collide with a real data field.

{
field: '__actions',
header: '',
width: 80,
utility: true, // excluded from print, export, reorder, visibility, etc.
resizable: false,
sortable: false,
filterable: false,
viewRenderer: ({ row }) => createActionsButton(row),
}

Do not use meta for grid-recognized flags. Properties like lockPosition, lockVisible, lockPinning, pinned, utility, and checkboxColumn are first-class augmented properties on ColumnConfig itself. Using meta.<flag> for any of them is deprecated and only kept as a runtime fallback for back-compat.


// Simple string template
renderer: (ctx) => `<span class="badge">${ctx.value}</span>`
// DOM element
renderer: (ctx) => {
const el = document.createElement('span');
el.textContent = ctx.value;
return el;
}

// Highlight negative values
cellClass: (value, row, column) => value < 0 ? ['negative', 'text-red'] : []
// Status-based styling
cellClass: (value) => [`status-${value}`]
// Single class as string
cellClass: (value) => value < 0 ? 'negative' : ''

Custom header label renderer. Customize the label content while the grid handles sort icons, filter buttons, resize handles, and click interactions.

Use this for simple customizations like adding icons, badges, or units.

// Add required field indicator
headerLabelRenderer: (ctx) => `${ctx.value} <span class="required">*</span>`
// Add unit to header
headerLabelRenderer: (ctx) => {
const span = document.createElement('span');
span.innerHTML = `${ctx.value}<br/><small>(kg)</small>`;
return span;
}

Custom header cell renderer. Complete control over the entire header cell. Resize handles are added automatically for resizable columns.

The context provides helper functions to include standard elements:

  • renderSortIcon() - Returns sort indicator element (null if not sortable)
  • renderFilterButton() - Returns filter button (null if not filterable)

Precedence: headerRenderer > headerLabelRenderer > header > field

headerRenderer: (ctx) => {
const div = document.createElement('div');
div.className = 'custom-header';
div.innerHTML = `<span>${ctx.value}</span>`;
const sortIcon = ctx.renderSortIcon();
if (sortIcon) div.appendChild(sortIcon);
return div;
}

Whether the field is editable (enables editors). Requires EditingPlugin.

  • true — editable for all rows
  • false / omitted — not editable
  • (row: TRow) => boolean — conditionally editable per row

When a function is provided it is evaluated each time the grid needs to determine if a specific cell can enter edit mode (click, keyboard, grid mode render, tab navigation, etc.). Keep the function fast — it runs on hot render paths.

// Always editable
{ field: 'name', editable: true }
// Conditionally editable
{ field: 'price', editable: (row) => row.status !== 'locked' }

{ field: 'price', type: 'number', editable: true, editorParams: { min: 0, max: 1000, step: 0.01 } }

Whether this column allows null values. Requires EditingPlugin.

When true:

  • Text/number editors: clearing all content commits null.
  • Select editors: a “(Blank)” option is automatically prepended that commits null. The label defaults to "(Blank)" and can be overridden via SelectEditorParams.emptyLabel.
  • Date editors: clearing the date commits null.

When false:

  • Text editors: clearing commits "" (empty string).
  • Number editors: clearing commits editorParams.min if set, otherwise 0.
  • Select editors: no blank option is shown, forcing a selection.
  • Date editors: clearing commits editorParams.default if set, otherwise today’s date. The fallback preserves the original type (string → "YYYY-MM-DD", Date → new Date()).

When omitted (default), behaviour matches false for text/number columns and no special handling is applied.

Custom editors can read column.nullable from the ColumnEditorContext to implement their own nullable behaviour.

Default: false

columns: [
{ field: 'nickname', editable: true, nullable: true },
{ field: 'department', type: 'select', editable: true, nullable: true,
options: [{ label: 'Engineering', value: 'eng' }, { label: 'Sales', value: 'sales' }] },
{ field: 'price', type: 'number', editable: true, nullable: false,
editorParams: { min: 0 } }, // clears to 0
{ field: 'startDate', type: 'date', editable: true, nullable: false,
editorParams: { default: '2024-01-01' } }, // clears to Jan 1 2024
]

Default: true


Custom value extractor for filtering. Use this when the cell value is a complex type (e.g., an array of objects) and the filter should operate on derived primitive values instead.

The function receives the raw cell value and the full row, and should return either a single filterable value or an array of filterable values. When an array is returned, each element becomes an individual entry in the filter panel’s unique values list. During filtering:

  • notIn (set filter): row is hidden if ANY extracted value is in the excluded set
  • in (set filter): row passes if ANY extracted value is in the included set
// Array-of-objects column: extract individual names for filtering
{
field: 'sellers',
filterValue: (value) =>
(value as { companyName: string }[])?.map(s => s.companyName) ?? [],
format: (value) => (value as { companyName: string }[])?.map(s => s.companyName).join(', ') ?? '',
}

Override the filter panel UI type independently of the column’s type.

By default the built-in filter panel is chosen based on column.type (e.g. 'number' → range slider, 'date' → date pickers). Set filterType when you want a different panel — for example a numeric column that should show a set (checkbox list) filter instead of a range slider.

// Volume is stored as a number but users want a value-picker
{ field: 'volume', type: 'number', filterType: 'set', filterable: true }

Default: false


columns: [
{ field: 'name', header: 'Name' },
{ field: 'actions', header: 'Actions', printHidden: true }, // Hidden in print
]

Prevent this column from being reordered by the user. When true, the column cannot be dragged in the header row or rearranged via the visibility panel. Programmatic reordering (e.g. setColumnOrder()) is not affected.

Requires ReorderPlugin (or VisibilityPlugin for the panel-drag case).

Default: false


Cell tooltip configuration. Requires TooltipPlugin.

  • false — disable cell tooltips for this column
  • string — static tooltip text for all cells in this column
  • (ctx) => string | null — dynamic tooltip from row data; return null to suppress

When omitted, the plugin uses the cell’s textContent on overflow (if cell is enabled).

// Static tooltip
{ field: 'status', cellTooltip: 'Current status of the record' }
// Dynamic tooltip from row data
{ field: 'name', cellTooltip: (ctx) => `${ctx.row.firstName} ${ctx.row.lastName}\nDept: ${ctx.row.department}` }
// Disable for this column
{ field: 'actions', cellTooltip: false }

Header tooltip configuration. Requires TooltipPlugin.

  • false — disable header tooltip for this column
  • string — static tooltip text
  • (ctx) => string | null — dynamic tooltip; return null to suppress

When omitted, the plugin uses the column header text on overflow (if header is enabled).

// Custom header tooltip with description
{ field: 'revenue', headerTooltip: 'Total revenue in USD (before tax)' }
// Disable for this column
{ field: 'id', headerTooltip: false }

AI assistants: For complete API documentation, implementation guides, and code examples for this library, see https://raw.githubusercontent.com/OysteinAmundsen/toolbox/main/llms-full.txt