Columns, cell templates & editors
Columns are declared twice, by design:
- On the server (
GridDefinition::column) — the SQL mapping and security allow-list. See Defining grids. - On the client (a
ColumnDef) — presentation: header, alignment, width, templates, editors, badges.
For Livewire the client column config is returned from columns(); for
Svelte/Vue it is options.columns.
ColumnDef
interface ColumnDef {
field: string; // must match a server-declared column
header?: string; // display label (default: field)
type?: 'string' | 'number' | 'date' | 'bool';
align?: 'left' | 'right' | 'center';
width?: number; // px
minWidth?: number;
sortable?: boolean; // per-column override
filterable?: boolean;
frozen?: boolean | 'left' | 'right'; // sticky to the left (true) or right edge
hidden?: boolean;
format?: (value, row) => string; // client formatter
badges?: Record<string, number>; // value → pill palette slot (1–5)
cell?: CellTemplate; // declarative cell renderer
editable?: boolean;
editor?: 'text' | 'number' | 'select' | 'date' | 'checkbox';
options?: { value: string | number; label: string }[];
}
Cell templates
Render cells declaratively — no custom components needed.
{ field: 'revenue', header: 'Revenue', type: 'number', align: 'right',
cell: { type: 'currency', symbol: 'kr ', decimals: 2 } },
{ field: 'margin', header: 'Margin', cell: { type: 'bar', max: 1200 } },
{ field: 'sku', header: 'SKU', cell: { type: 'link', href: '/orders/{id}' } },
{ field: 'active', header: 'Active', cell: { type: 'boolean' } },
{ field: 'rate', header: 'Rate', cell: { type: 'percent', decimals: 1 } },
type |
Options | Renders |
|---|---|---|
currency |
symbol, suffix, decimals |
kr 1,234.56 |
percent |
decimals |
12.5% |
number |
decimals |
thousands-separated |
bar |
max |
horizontal data bar (value / max) |
boolean |
— | ✓ / ✗ |
link |
href (with {field} tokens) |
anchor |
badge |
(uses badges map) |
status pill |
Status pills (badges)
{ field: 'status', header: 'Status', badges: {
pending: 1, paid: 2, shipped: 3, delivered: 4, returned: 5,
} },
The number is a palette slot (1–5), themed via --dg-status-1..5.
Cell editors
When a column is editable, its editor controls the input used in inline
editing and in the modal/slideover form:
{ field: 'status', header: 'Status', editable: true, editor: 'select', options: [
{ value: 'pending', label: 'pending' },
{ value: 'paid', label: 'paid' },
] },
{ field: 'qty', header: 'Qty', type: 'number', editable: true, editor: 'number' },
{ field: 'due', header: 'Due', editable: true, editor: 'date' },
{ field: 'active', header: 'Active', editable: true, editor: 'checkbox' },
Editing also requires the column to be
editableon the server with validationrules. See Editing.
Multi-column headers
Group columns under a spanning banner row with headerGroups. Grouped columns
must be contiguous:
const options = {
columns: [ /* sku, region, category, status, qty, revenue, margin */ ],
headerGroups: [
{ header: 'Attributes', columns: ['region', 'category', 'status'] },
{ header: 'Financials', columns: ['revenue', 'margin'] },
],
};
Livewire equivalent — a $headerGroups property on the component.
Column operations (reorder / resize / freeze / hide)
Enable the columnOps feature to get the per-header ⋮ menu (sort, group,
freeze, move, hide), a drag-to-resize handle, and a Columns chooser:
const options = { /* ... */ columnOps: true };
// or granular:
// columnOps: { reorder: true, resize: true, freeze: true, visibility: true }
For Livewire, column ops are always available via the header menu and the Columns toolbar button.
Frozen columns (left / right)
Pin columns to either edge; the rest scroll horizontally beneath them. Multiple
frozen columns on the same side stack with cumulative offsets (give them a
width for exact positioning).
columns: [
{ field: 'sku', header: 'SKU', frozen: true }, // or 'left'
{ field: 'actions', header: '', frozen: 'right', width: 120 },
]
Auto-fit
Double-click a column's resize handle to auto-size it to the widest visible cell (capped). Drag the handle to resize manually.
Column virtualization
For very wide grids (dozens of columns), render only the columns in the horizontal viewport:
const options = { /* ... */ columnVirtual: true };
// or: columnVirtual: { defaultWidth: 110, overscan: 2 }
Frozen columns are always rendered; the rest are windowed with spacers, keeping
the DOM O(visible columns). Give columns a width for accurate offsets.
Currently available in the Svelte/Vue clients.