Columns

Define fields, headers, sizing, renderers, and column behavior.

Columns describe how row data is read, displayed, edited, filtered, and written back.

import { Grid, type ColumnDef } from '@witbybit/wit-grid-react';

type Person = {
	id: string;
	name: string;
	team: string;
	score: number;
};

const columns: ColumnDef<Person>[] = [
	{ field: 'name', header: 'Name', width: 180 },
	{ field: 'team', header: 'Team', width: 160 },
	{ field: 'score', header: 'Score', type: 'number', width: 120 },
];

export function PeopleGrid({ rows }: { rows: Person[] }) {
	return <Grid rows={rows} columns={columns} getRowId={(row) => row.id} />;
}

Required Shape

Every column needs:

FieldTypeNotes
fieldstringStable data path and column identity. Dot paths are supported for nested values.
headerstringVisible column header text.

Use stable field values. They are used by state, events, filtering, sorting, selection ranges, clipboard, and persistence. Use colId only when the public API identity needs to differ from the row data path.

Sizing And Visibility

const columns = [
	{ field: 'name', header: 'Name', width: 220, minWidth: 140, maxWidth: 360 },
	{ field: 'internalNotes', header: 'Notes', hide: true },
	{ field: 'status', header: 'Status', pinned: 'left' },
];

width must be a positive finite number. minWidth and maxWidth constrain user resizing. pinned is an initial setting; use the grid API for runtime pin changes.

Values

By default, the grid reads row[field]. Dot paths read nested data.

const columns = [{ field: 'account.owner.name', header: 'Owner' }];

Use valueGetter when display data is derived.

const columns: ColumnDef<Person>[] = [
	{
		field: 'displayName',
		header: 'Display name',
		valueGetter: ({ row }) => `${row.name} (${row.team})`,
		valueGetterDependencies: ['name', 'team'],
	},
];

Use valueFormatter when the raw value should stay unchanged, but display/export text should be formatted.

const columns: ColumnDef<Person>[] = [
	{
		field: 'score',
		header: 'Score',
		valueFormatter: ({ value }) => `${Number(value).toFixed(1)} pts`,
	},
];

Writing Values

Use valueSetter to control edit commits.

const columns: ColumnDef<Person>[] = [
	{
		field: 'score',
		header: 'Score',
		valueSetter: ({ row, value }) => {
			const next = Number(value);
			if (!Number.isFinite(next) || next < 0) return false;
			row.score = next;
			return true;
		},
	},
];

Return false to reject a commit. Return a promise when validation depends on asynchronous work. Call abort() for an immediate rollback.

Column Types

Wit Grid ships built-in type registry entries for:

TypeBehavior
checkboxCheckbox renderer.
dateDate renderer and editor.
numberNumber renderer and editor.

Register custom types with the columnTypes grid prop.

import { Grid, numberColumnType, dropdownColumnType } from '@witbybit/wit-grid-react';

const columnTypes = {
	usd: numberColumnType({ prefix: '$', decimals: 2, locale: true }),
	status: dropdownColumnType([
		{ value: 'Active', color: 'emerald' },
		{ value: 'Paused', color: 'amber' },
	]),
};

const columns = [
	{ field: 'revenue', header: 'Revenue', type: 'usd' },
	{ field: 'status', header: 'Status', type: 'status' },
];

<Grid rows={rows} columns={columns} columnTypes={columnTypes} />;

Helper exports include numberColumnType, multiSelectColumnType, and dropdownColumnType.

Renderers And Editors

Use renderer to control cell display. Use cellEditor to control edit UI.

const columns: ColumnDef<Person>[] = [
	{
		field: 'team',
		header: 'Team',
		renderer: { kind: 'react', component: TeamBadge },
		cellEditor: TeamEditor,
	},
];

Renderer specs support text, DOM, React, and imperative React modes. For high-frequency cells, renderer capabilities control scroll behavior with primitive, live, freeze, text-impostor, or html-snapshot presentation — see Renderer Capabilities for the full tradeoff and when to use each.

Filtering And Sorting

const columns: ColumnDef<Person>[] = [
	{ field: 'name', header: 'Name', sortable: true, filterType: 'text' },
	{ field: 'score', header: 'Score', sortable: true, filterType: 'number' },
	{ field: 'team', header: 'Team', filterType: 'set' },
];

Prefer filterDef for new filter UI. filterType and filterValues are still supported and normalized internally.

Clipboard

Use onCopy and onPaste when clipboard text should not be the same as the raw cell value. See Clipboard for the grid-level copy/paste API and event payloads.

const columns: ColumnDef<Person>[] = [
	{
		field: 'score',
		header: 'Score',
		onCopy: ({ value }) => String(value ?? ''),
		onPaste: ({ pastedText }) => Number(pastedText),
	},
];

Header Groups

Group related columns under a shared spanning header label with headerGroup. Columns with the same label at the same position are merged into one spanning header cell automatically — no separate registration step.

const columns: ColumnDef<Person>[] = [
	{ field: 'name', header: 'Name', width: 160 },
	{ field: 'q1Revenue', header: 'Q1', headerGroup: 'Revenue', width: 100 },
	{ field: 'q2Revenue', header: 'Q2', headerGroup: 'Revenue', width: 100 },
	{ field: 'q1Cost', header: 'Q1', headerGroup: 'Costs', width: 100 },
	{ field: 'q2Cost', header: 'Q2', headerGroup: 'Costs', width: 100 },
];

Nesting groups

headerGroup also accepts an array for nested group levels, outermost to innermost — headerGroup: ['Financials', 'Revenue'] renders Financials > Revenue > the leaf column header. There is no separate headerGroupLevel field; the array's order is the nesting.

Auto-sizing

Double-click a column's resize handle to auto-size it to fit its content. The same behavior is available programmatically through GridApi:

// Resize a single column to fit its widest cell (header included by default)
api.autoSizeColumn('price');

// Resize all visible columns at once
api.autoSizeAllColumns();

// With options
api.autoSizeColumn('name', { padding: 24, includeHeader: true });
api.autoSizeAllColumns({ padding: 16, minWidth: 60, maxWidth: 400 });
OptionTypeDefaultDescription
paddingnumber16Extra pixels added to the measured content width.
includeHeaderbooleantrueInclude the header cell text in the width measurement.
minWidthnumber—Clamp the result to at least this many pixels.
maxWidthnumber—Clamp the result to at most this many pixels.

autoSizeAllColumns accepts the same options and applies them uniformly to every visible column.

Capabilities

Column capability callbacks allow per-cell or per-column rules.

const columns: ColumnDef<Person>[] = [
	{
		field: 'score',
		header: 'Score',
		canEdit: ({ row }) => row.team !== 'Locked',
		canSort: () => true,
		canExport: () => true,
	},
];

Capability callbacks exist for editing, selection, copy, paste, grouping, fill, sort, filter, pin, resize, delete, expand, drag, column movement, and export.

Edit on GitHub

Last updated on

On this page