# Column definitions

A ColumnDef tells SvGrid how to read a value out of a row, how to render it, and which features apply to it. The grid below is built from a handful of ColumnDefs - look at the source to see how each shape maps to a column behaviour:

Minimal

import type { ColumnDef } from 'sv-grid-community'

type Person = { firstName: string; age: number; status: string }

const columns: ColumnDef<{}, Person>[] = [
  { field: 'firstName', header: 'First name' },
  { field: 'age',       header: 'Age' },
  { field: 'status',    header: 'Status' },
]

Properties

Property Type Purpose
id string Stable column id. Required when you use accessorFn and no field.
field keyof TData & string Reads row[key].
accessorFn (row) => unknown Computes the value.
header string | (ctx) => unknown String, or a function returning a renderSnippet / renderComponent.
cell (ctx) => unknown Same shape as header, for body cells.
footer string | (ctx) => unknown Footer cell.
editorType 'text' | 'number' | 'date' | 'datetime' | 'checkbox' Inline editor type.
format CellFormatConfig Built-in number, currency, percent, date, datetime formatters.
formatter (ctx) => string Custom formatter - runs after field / accessorFn.
columns ColumnDef[] Children - turns this column into a column group.
width number Initial width in pixels (overrides the grid's columnWidth).

See packages/sv-grid-community/src/core.ts.

Accessor vs. accessorFn

field is the common case. Use accessorFn when the value is computed or comes from a nested object:

const columns: ColumnDef<{}, Person>[] = [
  { field: 'firstName', header: 'First' },
  {
    id: 'fullName',
    header: 'Full name',
    accessorFn: (row) => `${row.firstName} ${row.lastName}`,
  },
]

Whenever you use accessorFn you must supply an id - there is no string key to derive one from.

Format vs. formatter vs. cell

You want Use
Locale-aware number / currency / percent / date format
A custom string transformation formatter
Custom HTML (avatars, pills, progress, sparklines) cell with renderSnippet

format is purely declarative and locale-aware - prefer it for anything numeric or temporal:

{ field: 'salary', header: 'Salary',
  format: { type: 'currency', currency: 'USD', options: { maximumFractionDigits: 0 } } }

{ field: 'joinedAt', header: 'Joined',
  format: { type: 'date', pattern: 'y-m-d' } }

{ field: 'utilization', header: 'Utilization',
  format: { type: 'percent', valueIsPercentPoints: true } } // 42 -> 42%

formatter runs after the accessor; the result is what gets displayed (and what gets copied to the clipboard during cell selection).

cell is the most powerful - see Cell components.

TypeScript

Pass the row type as the second generic; the column's field is then checked against the row's keys:

const columns: ColumnDef<{}, Person>[] = [
  { field: 'firstName' },   // ✅
  // { field: 'first_name' } // ✗ compile error
]

The first generic is the feature set - derive it from tableFeatures so feature-specific column properties light up:

const features = tableFeatures({ rowSortingFeature, columnFilteringFeature })
type Features = typeof features

const columns: ColumnDef<Features, Person>[] = [/* … */]

See also