Columns
Defining columns, width, pinning, visibility
Columns are the heart of the grid. Each column is one object in the columns array. EliteGrid uses a grouped config style: related settings live together in named groups (size, display, sort, filter, edit, value) so your autocomplete stays clean.
{
field: 'price', // required — the data key
header: 'Price', // required — the header text
size: { /* width settings */ },
display: { /* look & formatting */ },
sort: { /* sort behaviour */ },
filter: { /* filter behaviour */ },
edit: { /* editing behaviour */ },
value: { /* custom data access */ },
}
This chapter covers field, header, size, and display. The other groups have their own chapters (sorting, filtering, editing, formatting).
Why "grouped config"? A column can have dozens of settings. If they were all flat (
width,minWidth,pinned,sortable,filterType, …) the autocomplete list your editor pops up would be a wall of unrelated options. By nesting them undersize,display,sort, etc., typingsize: {only shows you width-related options. It is purely an organisational convenience — it does not change what the grid can do."Autocomplete" (also called IntelliSense) is the dropdown of suggestions your code editor shows as you type. It is powered by the TypeScript types, which is another reason passing
<Product>tocreateGridis worthwhile.
field and header (required)
field— the property name on your row. Type-checked against your data type. This is also the column's unique ID.header— the label shown at the top of the column.
{ field: 'firstName', header: 'First Name' }
fieldis doing two jobs. First, it tells the grid which value to read from each row (row.firstName). Second, it becomes that column's unique ID — the string you pass to API calls likeapi.setColumnWidth('firstName', 300)orapi.moveColumn('firstName', 0). Because of this, every column needs a distinctfield. (If you ever need two columns reading the same field — say a "computed" column — see Chapter 08, which usesvalue.getterto read a field without colliding on the ID.)
Column width — the size group
| Property | Type | Default | Meaning |
|---|---|---|---|
width |
number |
150 |
Fixed width in pixels |
minWidth |
number |
50 |
Smallest the user can drag it |
maxWidth |
number |
— | Largest the user can drag it |
flex |
number |
— | Flexible width weight (see below) |
resizable |
boolean |
true |
Can the user drag to resize? |
Fixed width
{ field: 'id', header: 'ID', size: { width: 80 } }
Fixed (width) vs. flexible (flex) — which to use?
- Use
widthwhen a column should always be the same number of pixels regardless of screen size — an ID, an icon, a short status badge. - Use
flexwhen a column should grow and shrink to fill whatever space is left after the fixed columns are placed — names, emails, descriptions.
You can freely mix the two: give the narrow columns a width and let the wide ones share the rest with flex.
Flexible width with flex
flex makes columns share leftover space proportionally, like CSS flexbox. A column with flex: 2 gets twice the space of a column with flex: 1.
columns: [
{ field: 'name', header: 'Name', size: { flex: 2, minWidth: 120 } },
{ field: 'email', header: 'Email', size: { flex: 3, minWidth: 160 } },
{ field: 'role', header: 'Role', size: { flex: 1 } },
]
Worked example. Suppose the grid is 600px wide and none of the three columns above has a fixed width. The flex weights are 2 + 3 + 1 = 6 total parts, so each "part" is 600 / 6 = 100px. The columns therefore become 200px, 300px, and 100px. If you later add a fixed width: 120 column, that 120px is subtracted first and the remaining 480px is split by the same 2:3:1 ratio.
Tip: Always pair
flexwith aminWidthso columns never collapse to nothing on small screens. Without it, aflex: 1column on a very narrow grid can shrink until its text is unreadable.
Lock a column from resizing
{ field: 'id', header: 'ID', size: { width: 60, resizable: false } }
The display group
| Property | Type | Default | Meaning |
|---|---|---|---|
pinned |
'left' | 'right' | null |
null |
Freeze the column to a side |
visible |
boolean |
true |
Show or hide the column |
cellClass |
string | (value, row) => string |
— | CSS class for body cells |
headerClass |
string |
— | CSS class for the header cell |
formatter |
(value, row) => string |
— | Format the displayed text (see Ch. 08) |
exportFormatter |
(value, row) => string |
— | Separate format for CSV export |
Pinning (frozen columns)
Pinning (also called freezing) keeps a column stuck to the left or right edge while the rest of the table scrolls sideways underneath it. It is the same idea as "freeze panes" in a spreadsheet. This is most useful when you have many columns: pin an ID or name column to the left so the user always knows which row they are looking at, and pin an "actions" column to the right so buttons stay reachable.
{ field: 'name', header: 'Name', display: { pinned: 'left' } }
{ field: 'actions', header: '', display: { pinned: 'right' } }
Pin sparingly. Every pinned column is one fewer column's worth of space for the scrolling area in the middle.
Hiding a column
Hidden columns still exist (and still export to CSV if you want) but aren't drawn. This is different from simply leaving the column out of the array: a hidden column can be toggled back on at runtime with the API (api.setColumnVisible), which is how you build a "show/hide columns" menu.
{ field: 'internalNotes', header: 'Notes', display: { visible: false } }
Conditional cell styling with cellClass
A CSS class is a name you attach to an element so a matching rule in your stylesheet (e.g. .cell-danger { … }) styles it. cellClass lets you attach one to body cells.
cellClass can be a plain string (applied to every cell in the column), or a function that runs once per cell and returns a class name based on that cell's value — perfect for colour-coding. The function receives the cell value and the whole row, so you can branch on either.
{
field: 'status',
header: 'Status',
display: {
cellClass: (value) =>
value === 'overdue' ? 'cell-danger' : 'cell-normal',
},
}
/* your app's CSS — EliteGrid does not define these names; you do */
.cell-danger { color: #dc2626; font-weight: 600; }
Changing columns at runtime
You don't pass new columns to re-configure the grid. Instead, use the Grid API:
api.setColumnVisible('internalNotes', true) // show a hidden column
api.setColumnWidth('name', 300) // resize programmatically
api.setColumnPinned('name', 'left') // pin/unpin
api.moveColumn('email', 0) // reorder to first position
You can also save and restore the whole column layout (widths, order, pinning, visibility):
const layout = api.getColumnState() // save (e.g. to localStorage)
api.applyColumnState(layout) // restore later
Why this matters:
getColumnState()returns a plain, serializable object describing how the user has arranged their columns. "Serializable" means it can be turned into a string withJSON.stringifyand stored anywhere — most commonly the browser'slocalStorage. On the next visit you read it back and callapplyColumnState()so the user's preferred layout is remembered:// Save whenever the layout changes localStorage.setItem('grid-layout', JSON.stringify(api.getColumnState())) // Restore on startup const saved = localStorage.getItem('grid-layout') if (saved) api.applyColumnState(JSON.parse(saved))
Full example — a well-configured column set
const grid = createGrid<Employee>({
columns: [
{
field: 'id',
header: 'ID',
size: { width: 70, resizable: false },
display: { pinned: 'left' },
},
{
field: 'name',
header: 'Name',
size: { flex: 2, minWidth: 140 },
display: { pinned: 'left' },
},
{
field: 'department',
header: 'Department',
size: { flex: 1, minWidth: 110 },
},
{
field: 'email',
header: 'Email',
size: { flex: 3, minWidth: 180 },
},
{
field: 'internalRating',
header: 'Rating',
display: { visible: false }, // hidden until toggled on
},
],
data: employees,
})
Next: 03 · Sorting