Export & API
Grid API
The grid's handle for actions that are not state, such as scrolling to a row, focusing a cell, saving a layout or patching live rows.
Most of the grid is driven by props: rows, columns, sort, filters. Some things are actions rather than state, with no resting value a prop could hold: scroll to this row, focus that cell, download the current view, save the layout. For those the grid hands you one object, the grid API, through onReady.
Each button calls one API method. Resize or move a column, then save the layout, change it again and restore it.
Getting the handle
onReady is called once, when the grid mounts, with the API object:
<script lang="ts">
import { Grid, type GridApi } from 'bo-grid';
let api = $state<GridApi>();
</script>
<button onclick={() => api?.scrollToRow('ORD-1042', 'center')}>Find order</button>
<Grid {rows} {columns} height={480} onReady={(a) => (api = a)} /> In React and Vue the same object is the component’s ref (ref.current in React, the template ref’s api in Vue); see React, Vue & more.
Methods
| Method | What it does |
|---|---|
scrollToRow(id, align?) | Scroll a row into view; align is 'nearest' (default), 'start', 'center' or 'end' |
focusCell(rowId, columnKey) | Focus and select one cell |
getFocusedCell() | The focused { row, column }, or null |
getSelectedRows() | The ticked rows (with rowSelection), in view order |
autosizeColumns(keys?) | Fit columns to their content, as the column menu’s Autosize does |
exportCSV(filename?) | Download the current view, after filters and sort, as CSV |
getState() / applyState(state) | Save and restore the user’s layout (below) |
patchRows(patches) | Write live values into rows in place; see Live updates |
refresh() | Re-run sort and filters against values changed in place |
flashCells(options?) | Flash cells on demand; see Cell flashing |
pinRow(id, pinned?) / getPinnedRowIds() | Pin rows above the scroll from code |
openFind(query?) | Open the find bar (with findBar) |
Methods that look a row up by id (scrollToRow, focusCell) return false when the row is not in the current view: filtered out, on another page, or in a grid backed by a server source.
Saving a layout
getState() returns the user’s layout as one plain, JSON-safe object: column order, widths, hidden columns, pinned columns, pinned rows, sorts and filters. Keep it wherever you keep user settings, per user or per workspace, and give it back with applyState(saved):
const saved = api.getState();
localStorage.setItem('orders-layout', JSON.stringify(saved));
// later
api.applyState(JSON.parse(localStorage.getItem('orders-layout') ?? 'null')); applyState fits the saved layout onto the columns as they are now:
- Entries for columns that no longer exist are dropped.
- A new column lands where it is declared, after the nearest column declared before it.
- Malformed fields are ignored rather than thrown on.
- A state from another
versionof the format is refused whole (applyStatereturnsfalse), because half a layout is worse than none.
The same rules are exported as reconcileState(saved, columnKeys), for a server that wants to apply them itself. For the simple case, persistKey saves the layout in the browser’s local storage with no code.
Also exported
Besides Grid and its types, the package exports helpers you can use on their own:
| Group | Exports |
|---|---|
| Components | Grid, Sparkline |
| Formatting | fmtPrice, fmtPercent, fmtVolume, fmtDate, fmtCurrency, relativeTime |
| Data | aggregate, pivot, sortRows, heatColor, resolveColumns, buildMergePlan, colSpanRow |
| Export and import | toCSV, exportCSV, exportXLSX, parseCSV, parseCSVMatrix, parseTSV, parseJSON, parseRows, rowsFromObjects, toHTMLTable, printTable, escapeHTML |
| Server data | createArraySource |
| Theming and text | themeVars, themePresets and the preset token maps, DEFAULT_LABELS, resolveLabels |
| Layout state | reconcileState, GRID_STATE_VERSION |
| Canvas | drawCandles, setupHiDpiCanvas (draw candlesticks on a canvas of your own) |
Realtime and trading helpers live in their own entries, bo-grid/realtime, bo-grid/trading and bo-grid/ladder. Every prop and method, with its type, is in the Props reference.
Reference
onReady (api: GridApi) => void Called once, when the grid mounts, with a handle for imperative actions: scrollToRow, focusCell, patchRows, getSelectedRows, autosizeColumns, exportCSV, getState / applyState and more.
Example
<Grid {rows} {columns} height={480} onReady={(a) => (api = a)} />persistKey string Default undefinedRemember the user's layout (column order, widths, hidden and pinned columns, pinned rows) in localStorage under this key.
scrollToRow (key: string | number, align?: ScrollAlign): boolean Scroll a row into view by its id; align is 'nearest' unless given. Returns false when the row is not in the current view (filtered out, not loaded, or unknown).
Example
api.scrollToRow(order.id, 'center');focusCell (rowKey: string | number, columnKey: string): boolean Focus (and select) one cell by row id and column key. Returns false when the row or column is not currently visible.
Example
if (!api.focusCell(row.id, 'qty')) api.scrollToRow(row.id);getFocusedCell (): { row: GridRow; column: ColumnDef } | null The focused cell's row and column, or null when no data cell has focus (nothing focused yet, or the focus is on a group row). For hotkeys that act on the row the user is on.
Example
const cell = api.getFocusedCell();
if (cell) openTicket(cell.row);getSelectedRows (): GridRow[] Ticked rows (needs rowSelection), in view order.
Example
const ids = api.getSelectedRows().map((row) => row.id);autosizeColumns (keys?: string[]): void Fit columns to their content, as the column menu's Autosize does. All resizable columns when keys is omitted. The other columns keep their widths (flex ones are held where they are).
Example
api.autosizeColumns(['sym', 'name']);exportCSV (filename?: string): Promise<void> Download the current view (after filter and sort) as CSV, named export.csv unless filename is given.
Example
await api.exportCSV(`orders-${new Date().toISOString().slice(0, 10)}.csv`);getState (): GridState Snapshot of the user's layout: order, widths, hidden, pins, sorts, filters. A plain object, safe to JSON.stringify and store.
Example
localStorage.setItem('orders-layout', JSON.stringify(api.getState()));applyState (state: unknown): boolean Restore a snapshot, reconciled against the columns as they are now. Returns false when the state is unusable (foreign version, not an object).
Example
const saved = localStorage.getItem('orders-layout');
if (saved) api.applyState(JSON.parse(saved));patchRows (patches: Iterable<readonly [string | number, Record<string, unknown>]>): number Realtime fast path: write [rowId, fields] patches into the rows in place and repaint only the rendered rows they change. Rows may be plain objects (no $state proxy) — far cheaper per write in a busy feed. Feed it a tick stream: createTickStream({ apply: (b) => api.patchRows(b) }). Sort and filter catch up on the next view change, as for any live data (or at once, with liveSort). A patch's id is the row's getRowId value (row.id unless set). Returns how many rows changed.
Example
api.patchRows([
[1, { last: 68.6, vol: 1_204_300 }],
[2, { last: 121.1 }],
]);refresh (): void Re-run filter and sort against the rows' current values. Values that change in place (a feed) never re-sort the view by themselves — that would mean re-sorting on every tick — so call this when the order should catch up, e.g. every second on a "top movers" board (or use liveSort).
Example
const timer = setInterval(() => api.refresh(), 1000);flashCells (opts?: FlashCellsOptions): number Flash cells on demand — a fill, an alert, a row to notice — with the tick animation, whether or not their values changed. Only rows on screen flash. Value cells flash (numbers, text); cells that draw their own content (badges, sparklines, components) do not. Returns how many rows it flashed.
Example
api.flashCells({ rows: [fill.orderId], dir: fill.side === 'buy' ? 'up' : 'down', ms: 800 });pinRow (key: string | number, pinned?: boolean): boolean Pin a row above the scroll (or unpin it with pinned: false), as the row menu does under rowPinning; pinned is true unless given. The row stays in the body too. Returns false when no row has that id.
Example
api.pinRow(row.id);getPinnedRowIds (): (string | number)[] Ids of the rows pinned at runtime, in pin order.
Example
for (const id of api.getPinnedRowIds()) api.pinRow(id, false);openFind (query?: string): void Open the find bar (findBar), optionally searching for query at once.
Example
api.openFind('VNM');