The Grid API
Controlling the grid from your own code
The Grid API is your remote control for the grid. It has 50+ methods to read and change data, columns, sorting, filters, selection, editing, pagination, scrolling, and export — all from your own code.
"API" = Application Programming Interface. In plain terms it's the agreed set of commands one piece of code offers to another. The Grid API is just an object full of methods (
api.setPage(2),api.exportCSV(), …). You don't reach inside the grid's internals; you call these published methods and the grid does the work.
Two ways to interact with the grid, and when to use each:
- The API (this chapter) — for doing things on demand: a button that exports CSV, code that jumps to page 2, a "select all" action. You call it.
- The data store (end of this chapter) — for displaying live grid state in your own UI: "3 of 100 selected". You subscribe to it and it calls you back.
Reach for the API when an action happens; reach for the store when you need to mirror grid state on screen.
How to get the API
Vanilla JS gives you two equally valid ways to get an API object for a grid, because there's no framework lifecycle forcing a particular order:
Option A — build it directly
buildGridAPI(grid) is a plain function: pass it the GridInstance from createGrid(), get an API object back, synchronously, any time — even before you've called mount().
import { createGrid, buildGridAPI, mount } from '@elitegrid/vanilla'
const grid = createGrid<User>({ columns, data })
const api = buildGridAPI(grid)
document.getElementById('export-btn')!.addEventListener('click', () => {
api.exportCSV()
})
mount(grid, document.getElementById('grid-container'))
Option B — receive it from onReady
mount() also builds an API object internally and hands it to your events.onReady callback once the grid has finished its first render — useful if you'd rather wire everything up in one place:
const grid = createGrid<User>({
columns,
data,
events: {
onReady: (api) => {
document.getElementById('export-btn')!.addEventListener('click', () => {
api.exportCSV()
})
},
},
})
mount(grid, document.getElementById('grid-container'))
Both options return an API object with the exact same methods — buildGridAPI(grid) is simply how mount() builds the one it hands to onReady. Reach for Option A when you want the API immediately (e.g. to wire up buttons before the grid has even mounted); reach for Option B when it's more convenient to do all your setup inside a single callback.
The complete API reference
Data
api.setData(rows) // replace all rows
api.getData() // → TData[] (the raw, unfiltered rows)
api.getDisplayedRows() // → TData[] (current page, after filter+sort)
api.refreshData() // re-run the pipeline / re-fetch from dataSource
api.addRow(row, index?) // add a row (optionally at an index)
api.updateRow(id, changes) // patch a row by ID
api.deleteRow(id) // delete one row
api.deleteRows(ids) // delete many rows in a single pass (fast)
getData()vsgetDisplayedRows()— the "pipeline". Internally the grid runs your rows through a pipeline: raw data → filter → sort → paginate → what's on screen.getData()returns the rows from the start of that pipeline (everything, untouched).getDisplayedRows()returns the rows at the end — already filtered, sorted, and limited to the current page, in the exact order shown. UsegetData()for "all my records"; usegetDisplayedRows()for "what the user is actually looking at right now".
updateRow(id, changes)— what "patch" means. You pass only the fields that changed, e.g.api.updateRow('42', { salary: 90000 }), and the grid merges them into the existing row. You don't have to send the whole row back.
Columns
api.setColumnVisible(id, visible)
api.setColumnWidth(id, width)
api.setColumnPinned(id, 'left' | 'right' | null)
api.moveColumn(id, toIndex)
api.getAllColumns() // → ColumnDef[] (everything you passed in)
api.getVisibleColumns() // → ColumnDef[] (currently visible)
api.getColumnState() // → ColumnState[] (widths, order, pinning, visibility)
api.applyColumnState(state) // restore a saved layout
Sort
api.setSortModel([{ columnId: 'name', direction: 'asc' }])
api.getSortModel()
api.clearSort()
Filter
api.setFilterModel(model)
api.getFilterModel()
api.setColumnFilter(columnId, filter)
api.clearColumnFilter(columnId)
api.clearFilters()
Selection
api.selectRow(id)
api.deselectRow(id)
api.toggleRow(id)
api.selectAll()
api.deselectAll()
api.isRowSelected(id) // → boolean
api.getSelectedRows() // → TData[]
api.getSelectedIds() // → Set<string>
Editing
api.startEditing(rowId, columnId)
api.stopEditing(save?) // true = save, false = discard
api.isEditing() // → boolean
api.getEditingCell() // → { rowId, columnId, rowIndex, columnIndex } | null
Pagination
api.setPage(page)
api.setPageSize(size)
api.nextPage()
api.previousPage()
api.firstPage()
api.lastPage()
api.getPaginationState() // → PaginationState
Scrolling
api.scrollToRow(rowId)
api.scrollToColumn(columnId)
api.scrollToCell(rowId, columnId)
Export
api.exportCSV(options?) // see Chapter 10
Lifecycle
api.destroy() // tear down the engine (rarely needed by hand)
The data store (advanced)
What does "subscribe" mean here? A subscription is a function you register once that gets called again automatically every time the thing you subscribed to changes — you don't have to keep asking "has it changed yet?" The API's
get…methods give you a snapshot (correct only at the instant you call them); the store gives you an ongoing feed.
Most apps only need createGrid + mount + the API. But if you want to watch grid state and keep your own UI in sync (e.g. show "3 of 100 rows selected" in your own toolbar), every GridInstance exposes its internal store at grid.kernel.store. It's organised into namespaces — one per concern:
grid.kernel.store.read('paginated')
// → { currentPage, pageSize, totalRows, totalPages, startRow, endRow, hasNextPage, hasPreviousPage }
const unsubscribe = grid.kernel.store.subscribe('paginated', (state) => {
console.log(`${state.totalRows} rows`)
})
// Later, when you no longer need updates:
unsubscribe()
| Namespace | State about |
|---|---|
viewport |
Which rows/columns are currently rendered, scroll position |
columns |
Column widths, order, visibility, pinning |
paginated |
Current page, page size, total rows/pages |
sorted |
The current sort model |
filtered |
The current filter model and match count |
raw |
The unfiltered row data and loading/error state |
selection |
Selected IDs, count, select-all state |
editing |
Whether a cell is editing, its value, validation state |
focus |
The currently focused cell |
store.read(namespace) gives you a one-off snapshot — useful for painting the initial state before your first update arrives. store.subscribe(namespace, callback) calls your callback again every time that namespace changes and returns an unsubscribe function; always keep a reference to it so you can stop listening later (see Cleaning up below). Chapter 05 and Chapter 06 both walk through worked examples of this pattern.
Cleaning up
An engine created with createGrid() lives until something explicitly tears it down — fine for a single grid that lives for the whole page. Three layers can be cleaned up independently, from lightest to heaviest:
const grid = createGrid<User>({ columns, data })
const dispose = mount(grid, container)
// 1. Unmount the DOM only, keep the engine alive (rare — you'd remount later)
dispose()
// 2. Unmount AND destroy the engine together — the common case when you're
// done with this grid for good (removing it from a page you're navigating away from)
const disposeAndDestroy = mount(grid, container, { destroyOnDispose: true })
disposeAndDestroy()
// 3. Destroy the engine directly, if you built it with buildGridAPI() and never mounted it
grid.kernel.destroy()
mount()'s dispose function does not destroy the engine by default — only its DOM and internal view listeners. Pass{ destroyOnDispose: true }when you callmount()if you want the same one dispose call to also tear down the kernel (unsubscribing every plugin from the event bus). This matters most in a page that mounts and unmounts the same grid's container repeatedly — a tab switcher, a modal that reopens — where forgetting it leaks one engine per open/close cycle.
Any unsubscribe() functions you got from grid.kernel.store.subscribe(...) or grid.kernel.eventBus.on(...) (see Chapter 11) are independent of mount()'s dispose function — call those yourself too when you remove the corresponding UI.
Next: 13 · Accessibility