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:

Open the live example: Quick start (Getting Started)

Minimal

import type { ColumnDef } from '@svgrid/grid'

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

const columns: GridColumns<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 fieldFn and no field.
field keyof TData & string Reads row[key].
fieldFn (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 / fieldFn.
columns ColumnDef[] Children - turns this column into a column group.
width number Initial width in pixels (overrides the grid's columnWidth).

See packages/grid/src/core.ts.

Accessor vs. fieldFn

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

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

Whenever you use fieldFn 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: GridColumns<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>[] = [/* … */]

Alignment follows the data

align is inferred from the editor type when you leave it out, which is why numbers land right without being told. Set it explicitly only when you disagree

<script lang="ts">
  import { SvGrid, type GridColumns, type SvGridApi } from '@svgrid/grid'

  type Person = {
    id: number
    name: string
    department: string
    city: string
    age: number
    salary: number
  }

  const people: Person[] = [
    { id: 1, name: 'Ada Lovelace',   department: 'Engineering', city: 'London',   age: 36, salary: 142000 },
    { id: 2, name: 'Grace Hopper',   department: 'Engineering', city: 'New York', age: 45, salary: 168000 },
    { id: 3, name: 'Linus Torvalds', department: 'Platform',    city: 'Portland', age: 54, salary: 155000 },
    { id: 4, name: 'Radia Perlman',  department: 'Networking',  city: 'Seattle',  age: 49, salary: 161000 },
    { id: 5, name: 'Barbara Liskov', department: 'Platform',    city: 'Boston',   age: 52, salary: 172000 },
  ]

  const columns: GridColumns<Person> = [
    { field: 'name',   header: 'Name',   width: 190 },
    { field: 'city',   header: 'City',   width: 130, align: 'center' },
    { field: 'age',    header: 'Age',    width: 90,  editorType: 'number' },
    { field: 'salary', header: 'Salary', width: 140, align: 'right',
      format: { type: 'currency', currency: 'USD' } },
  ]
</script>

<SvGrid data={people} {columns} />

Per-column opt-outs

Turning a capability on at the grid level does not force it on every column. sortable: false and filterable: false take a column out - useful for an actions column, or a note nobody should sort by.

<script lang="ts">
  import { SvGrid, type GridColumns, type SvGridApi } from '@svgrid/grid'

  type Person = {
    id: number
    name: string
    department: string
    city: string
    age: number
    salary: number
  }

  const people: Person[] = [
    { id: 1, name: 'Ada Lovelace',   department: 'Engineering', city: 'London',   age: 36, salary: 142000 },
    { id: 2, name: 'Grace Hopper',   department: 'Engineering', city: 'New York', age: 45, salary: 168000 },
    { id: 3, name: 'Linus Torvalds', department: 'Platform',    city: 'Portland', age: 54, salary: 155000 },
    { id: 4, name: 'Radia Perlman',  department: 'Networking',  city: 'Seattle',  age: 49, salary: 161000 },
    { id: 5, name: 'Barbara Liskov', department: 'Platform',    city: 'Boston',   age: 52, salary: 172000 },
  ]

  const columns: GridColumns<Person> = [
    { field: 'name',       header: 'Name',       width: 190 },
    { field: 'department', header: 'Department', width: 160 },
    // Sortable everywhere except here.
    { field: 'city',       header: 'City',       width: 140, sortable: false, filterable: false },
    { field: 'salary',     header: 'Salary',     width: 140,
      format: { type: 'currency', currency: 'USD' } },
  ]
</script>

<SvGrid data={people} {columns} sortable filterable filterMode="row" />

See also

Live examples

  • Quick start - A realistic 25-row × 9-column grid with sort, filter, selection, inline editing, and column resize all enabled.
  • Sort, filter, paginate - Three most-asked-for features wired together against ~5k rows.

Related articles