---
id: "packages/active-grid/api/columns"
title: "Columns"
description: "ColumnFilterType, FilterComponentProps, IntervalInputProps, GridColumnDef and 7 more: generated from the source of active-grid."
url: "/packages/active-grid/api/columns"
locale: "en"
revision: "g0mvjxwj5c-88bdbe1c:columns"
---

# Columns

Exported by `@workspace/active-grid`. Generated from the package source; part of [the index of every export](/packages/active-grid/api).

## Functions, hooks and constants

**hasAccessorFn**: `<TData, TValue>(column: GridColumnDef<TData, TValue>) => column is GridAccessorFnColumnDef<TData, TValue>`

The same narrowing for the other accessor: does this definition compute its
value from the row, and if so with which function?

Symmetrical to {@link hasAccessorKey} and needed for the same reason. A
definition read back from an array is the union, and a caller that wants to
invoke the accessor has to establish that there is one; the alternative is an
assertion that keeps compiling after the field moves.

**hasAccessorKey**: `<TData, TValue>(column: GridColumnDef<TData, TValue>) => column is GridAccessorKeyColumnDef<TData, TValue>`

The one narrowing for the union above: does this definition read its value
by naming a row field, and if so what is that field called?

Reading `accessorKey` off a column definition is a real need — hydration
prunes a persisted sort against it, the column-type presets key on it. The
alternative, a local `column as {accessorKey?: string}`, is an assertion in
a position where a test is available, and it silently keeps working when
the field is renamed. This is the test.

It returns the narrowed type rather than a boolean so that the caller reads
the field off a definition the compiler agrees has one.

## Types

**ColumnFilterType**

```ts
type ColumnFilterType =
  'text' | 'number' | 'date' | 'select' | 'boolean' | 'interval';
```

**FilterComponentProps**

```ts
interface FilterComponentProps {
  column: Column;
  placeholder?: string;
}
```

Props passed to a custom filter component registered via `meta.customFilter`.
The component receives the column instance for reading/writing filter state,
and an optional placeholder string derived from the column header.

**IntervalInputProps**

```ts
interface IntervalInputProps {
  from: number | null;
  to: number | null;
  onChange: (next: {from: number | null; to: number | null}) => void;
  placeholder?: string;
}
```

Props for a custom boundary-input inside the built-in `interval` filter
(`meta.intervalInput`). The interval filter owns the mode toggle, the popover
and the apply/clear plumbing; this component owns only the entry of the two
numeric bounds. `from`/`to` are plain numbers (or `null` when unset); the
implementation reports new bounds via `onChange`. This is the seam where a
host plugs in a domain-specific entry (a year picker, a price range) without
the grid knowing what the numbers mean.

**GridColumnDef**

```ts
type GridColumnDef<TData, TValue = any> =
  | GridAccessorKeyColumnDef<TData, TValue>
  | GridAccessorFnColumnDef<TData, TValue>
  | GridGroupColumnDef<TData, TValue>;
```

A column definition.

## It is a union, and that is what makes it a contract

Three kinds, and an invalid definition matches none of them. One consequence
for whoever READS a definition back: `accessorKey` is not reachable on the
union, because two of the three kinds do not have it. Narrow with
{@link hasAccessorKey} rather than asserting the field into existence.

## The one widening in this contract

`TValue` defaults to `any`. This is the only place in the public column
contract where that type appears, it is named here, and it is a deliberate
position rather than a convenience.

The reason is the ARRAY ANNOTATION. `GridColumnDef<Row>[]` is how a column
set is declared, and one such array holds columns whose values are a string,
a number and nothing at all. A definition that knows its own value type —
everything `createColumnHelper` builds, and everything written as
`GridColumnDef<Row, number>` — reaches that array through `cell`, `header`
and `footer`, whose contexts carry the value type in a callback PARAMETER.
Under `strictFunctionTypes` a parameter is checked contravariantly, so a
definition over `string` is not a definition over `unknown`, and the array
would refuse every column that knows what it holds: with `unknown` in this
position the helper's own output cannot enter an annotated array at all.

What the widening does NOT touch: the metadata key space
stays closed, a metadata value of the wrong type is still refused, a key
naming no field of the row is still refused, a nested entry is still a
definition, and a primitive is still not a column. Those are the negative
fixtures in `kernel/surface/__tests__/contract/type-acceptance.contract.test.ts`, and they are
asserted against this default rather than against a stricter instantiation.

Naming `TValue` explicitly gives the strict contract for a single column:
`GridColumnDef<Row, string>` refuses a renderer shaped for a number. The
widening is the DEFAULT, not the type.

**GridColumnDefaults**

```ts
interface GridColumnDefaults<TData = DefaultRowData, TValue = unknown> {
  size?: number;
  minSize?: number;
  maxSize?: number;
  enableSorting?: boolean;
  enableResizing?: boolean;
  enablePinning?: boolean;
  enableHiding?: boolean;
  meta?: Partial<GridColumnMeta<TData, TValue>>;
}
```

**GridColumnMeta**

```ts
interface GridColumnMeta<TData, TValue> {
  label?: string;
  headerAlign?: 'left' | 'center' | 'right';
  cellAlign?: 'left' | 'center' | 'right';
  headerClassName?: string;
  cellClassName?: string | ((value: TValue, row: TData) => string | undefined);
  headerClass?: (context: HeaderContext<TData, TValue>) => string;
  filterType?: ColumnFilterType;
  customFilter?: React.ComponentType<FilterComponentProps>;
  intervalInput?: React.ComponentType<IntervalInputProps>;
  filterOptions?: {label: string; value: string}[];
  emptyValueKey?: string;
  allowedOperators?: FilterOperator[];
  operatorPreset?: BuiltInOperatorPreset | (string & {});
  defaultFilterOperator?: FilterOperator;
  filterSearchable?: boolean;
  filterShowSelectAll?: boolean;
  booleanFilterLabels?: {
    all?: string;
    true?: string;
    false?: string;
  };
  booleanFilterVariant?: 'text' | 'icon';
  dateFilterPresets?: DateFilterPreset[] | false;
  dateFilterMode?: 'single' | 'range' | 'both';
  dateFilterMinDate?: string | Date;
  dateFilterMaxDate?: string | Date;
  suppressFilterIcon?: boolean;
  editable?: boolean | ((row: TData) => boolean);
  editType?: ColumnEditType;
  validate?: (
    value: unknown,
    row: TData,
  ) => string | undefined | Promise<string | undefined>;
  editOptions?: {label: string; value: string}[];
  valueParser?: (value: string) => unknown;
  aggregationFn?: 'sum' | 'avg' | 'count' | 'min' | 'max';
  aggregationLabel?: string;
  statusTones?: Partial<Record<string, GridStatusTone>>;
  semanticType?: 'currency' | 'number' | 'percent' | 'date' | 'dateTime';
  datePreset?: DatePreset;
  currencyCode?: string;
  currencyKey?: string;
  currencyDecimals?: number;
  numberFormat?: NumberFormat;
  percentSource?: 'fraction' | 'percentage-points';
  percentPreset?: PercentFormat;
  truncate?: boolean;
  textDirection?: GridTextDirection;
  valueFormatter?: (value: TValue, data: TData) => React.ReactNode;
  exportValue?: (value: TValue, data: TData) => unknown;
  cellClassRules?: Record<string, (value: TValue, row: TData) => boolean>;
  cellStyle?: (value: TValue, row: TData) => React.CSSProperties | undefined;
  headerTooltip?: string;
  tooltipField?: keyof TData;
  tooltipValueGetter?: (value: TValue, row: TData) => string;
  showTooltipOnTruncate?: boolean;
  wrapText?: boolean;
  autoHeight?: boolean;
  colSpan?: number | ((row: TData) => number);
  enableResizing?: boolean;
  autoSizeMaxBodyWidth?: number;
  enablePinning?: boolean;
  flex?: number;
  suppressSizeToFit?: boolean;
  locked?: boolean;
  responsivePriority?: number;
  headerMenu?: GridHeaderMenuConfig;
  suppressHeaderMenuButton?: boolean;
  headerRenderer?: (props: HeaderRendererProps) => React.ReactNode;
  isSystemColumn?: boolean;
  _hasCustomCell?: boolean;
  _serverFilterCapability?: import('../utils/capability-contract').GridServerFilterCapability;
}
```

Per-column meta configuration. Fields are grouped by purpose; each
group is delimited by a section-marker comment. Categories:

1. **Display & alignment** — label, header/cell alignment, classNames.
2. **Filtering** — filterType, customFilter, operator config, filterOptions, date-filter config.
3. **Editing** — editable, editType, validate, editOptions, valueParser.
4. **Aggregation** — aggregationFn, aggregationLabel.
5. **Cell formatting & tooltips** — statusTones, valueFormatter, cellClassRules, tooltips, truncate, wrap.
6. **Layout** — colSpan, resize/pin, autoSize, flex, responsivePriority, locked.
7. **Header customization** — headerMenu, headerRenderer, suppressHeaderMenuButton.
8. **System** — isSystemColumn, suppressFilterIcon, internal `_*` fields.

A callback that is handed the row's record (`TData`) is asked about the cells
of records only. A row grouping generated stands for no record, so on it
such a callback is not asked, and the grid does what it does without one:
no edit, no class or style from it, no tooltip from it, no span.

**GridColumnTypes**

```ts
type GridColumnTypes<TData = DefaultRowData> = Record<
  string,
  Partial<GridColumnDef<TData>>
>;
```

**HeaderRendererProps**

```ts
interface HeaderRendererProps {
  label: React.ReactNode;
  isSortable: boolean;
  sortDirection: 'asc' | 'desc' | false;
  sortIndex: number;
  isMultiSort: boolean;
  onToggleSort?: (event: unknown) => void;
  isFiltered: boolean;
  canResize: boolean;
  sortIndicator: React.ReactNode;
  menuElement: React.ReactNode;
  resizeHandle: React.ReactNode;
}
```

Props passed to a custom `meta.headerRenderer`. Provides everything
needed to render sort indicators, filter icons, resize handles, and
menu buttons inside a fully custom header cell layout.

**GridStatusTone**

```ts
type GridStatusTone =
  'neutral' | 'info' | 'success' | 'warning' | 'danger';
```

The five generic status tones a status cell draws with
(`meta.statusTones`, `<StatusCell tone>`).

UI semantics, not domain semantics: the grid does not know what `LAPSED`
or `PENDING_APPROVAL` means; the column maps each domain value onto a tone.
There is deliberately no `pending` tone: waiting without attention needed
is `neutral`, waiting that needs attention is `warning`. Each tone paints
with the `--grid-status-<tone>` tokens, which a host maps onto its own
status colours.
