highgrid
v1.0.1
Published
Production-ready data grid library in vanilla JavaScript
Downloads
1,543
Maintainers
Readme
HighGrid
HighGrid is a vanilla JavaScript data grid library. It covers the pieces you usually need in a real table: virtual scrolling, paging, infinite loading, grouping, tree data, live updates, plugins, and custom cell rendering.
Other Languages: 한국어 문서 (Korean)
Demo
Table of Contents
A categorized guide map to quickly locate details about HighGrid's extensive feature set. Click on any topic to jump directly to its section.
1. Setup & Fundamentals
- 1. Overview
- 2. Installation
- 3. Quick Start
- 4. Column Definitions
- 5. Cell Renderer
- 6. Core Options
- 7. Overlay States and Accessibility
- 20. Header Groups
- 23. TypeScript Support
- 27. Before You Ship
- 50. Auto-numbering Row Column (Row number)
- 52. Column Inline Filter Row
2. Data Operations & Updates
- 8. Data Mutation API
- 12. Live Updates
- 18. Column Runtime API
- 24. State Query API
- 30. Column Aggregation (Status Bar)
- 33. Row Pinning
- 40. Status Bar
- 51. Cell Flash Animation
3. Filtering, Sorting & Grouping
- 9. Sorting, Filtering, Selection
- 10. Grouping and Tree Data
- 11. Pagination and Infinite Scroll
- 31. Advanced Filter (AND/OR Tree)
- 32. Pivot Mode
- 41. Master-Detail (Expandable Rows)
- 43. Multi-column Sorting (Shift+Click)
- 49. Advanced Filter GUI Builder
- 57. Multi-level Group Aggregations
- 58. Web Worker Background Data Pipeline
4. Cell Interactions & Editing
- 13. Cell Editing, Validation, and Clipboard
- 29. Custom Cell Editors
- 34. Undo / Redo
- 35. Row Drag & Drop
- 36. Range Selection
- 47. 2D Rectangular Range Copy/Paste
- 48. Functional Cell Edit Locking
- 53. Keyboard Navigation Completed
- 55. Fill Handle (Cell Autofill)
- 59. Basic Formulas (=SUM, =AVG)
5. Styling, Events & Tooling
- 14. Events
- 15. CSV/Excel Export and Context Menus
- 16. Plugins
- 17. Persisting Column State
- 19. Variable Row Height
- 22. Theming
- 37. Conditional Formatting
- 38. Sparkline Plugin (Inline Charts)
- 39. XLSX Export Plugin
- 42. Print
- 54. Side Panel Drag & Drop Column Reordering
- 56. Rich HTML Tooltip (Custom Popups)
6. Framework Integrations
7. Development & Running Examples
1. Overview
HighGrid is a vanilla JavaScript data grid for teams that want a solid default table without bringing in a large framework dependency. It includes virtual scrolling, client/server pagination, infinite loading, grouping, tree data, runtime updates, plugins, custom cell renderers, and a built-in side panel.
2. Installation
npm install highgridImport both the library and the stylesheet.
import { createGrid } from "highgrid";
import "highgrid/styles/grid.css";3. Quick Start
<div id="app" style="height: 600px;"></div>import { createGrid } from "highgrid";
import "highgrid/styles/grid.css";
const rows = [
{ id: 1, name: "Alice", team: "Red", score: 1200 },
{ id: 2, name: "Bob", team: "Blue", score: 980 },
];
const columns = [
{ id: "name", field: "name", headerName: "Name", width: 180 },
{ id: "team", field: "team", headerName: "Team", width: 120 },
{
id: "score",
field: "score",
headerName: "Score",
width: 120,
align: "right",
},
];
const grid = createGrid("#app", {
rowKey: "id",
columns,
rows,
rowHeight: 40,
});Tips:
- You can pass a selector string directly, like
createGrid('#app', options). - Give the container an explicit height for virtual scrolling.
- Use a stable and unique
rowKeywhenever possible.
4. Column Definitions
Common column properties:
id: unique column idfield: row field nameheaderNameorheader: header labelwidth: default widthalign:left,center,righttype: sorting/comparison hint such asnumberordaterenderer: custom cell rendererformatter: value formatterpinned:leftorrightminWidth,maxWidth: resize limits
Example:
const columns = [
{ id: "name", field: "name", headerName: "Name", width: 180 },
{
id: "status",
field: "status",
headerName: "Status",
width: 140,
renderer: ({ value }) => {
const badge = document.createElement("span");
badge.textContent = value;
badge.className = `status status-${String(value).toLowerCase()}`;
return badge;
},
},
];5. Cell Renderer
A cell renderer is called as renderer({ value, row, def, state }).
Return rules:
- Return an
HTMLElementto inject real DOM content. - Return a string or number for text rendering.
- Return
nullorundefinedfor an empty result.
const scoreColumn = {
id: "score",
field: "score",
headerName: "Score",
renderer: ({ value }) => {
const wrap = document.createElement("div");
wrap.style.display = "flex";
wrap.style.justifyContent = "space-between";
const label = document.createElement("strong");
label.textContent = String(value);
const meter = document.createElement("span");
meter.style.width = `${Math.min(100, Number(value) / 20)}%`;
meter.style.height = "6px";
meter.style.background = "#0f4c81";
meter.style.borderRadius = "999px";
wrap.append(label, meter);
return wrap;
},
};6. Core Options
const grid = createGrid(container, {
rowKey: "id",
columns,
rows,
rowHeight: 40,
variableRowHeight: false,
selectable: true,
selectionMode: "multiple",
tableId: "orders-grid",
sidePanel: {
enabled: true,
defaultTab: "columns",
defaultOpen: false,
quickFilterFields: ["name", "team", "status"],
},
pagination: {
mode: "client",
pageSize: 25,
},
infiniteScroll: {
mode: "client",
initialLoadSize: 100,
loadMoreSize: 50,
},
liveUpdates: {
enabled: true,
maxRows: 1000,
rowAnimationEnabled: true,
},
});7. Overlay States and Accessibility
const grid = createGrid("#app", {
rowKey: "id",
columns,
rows: [],
renderLoadingState: () => "<div>Loading...</div>",
renderEmptyState: () => "<div>No rows yet.</div>",
renderErrorState: ({ message }) => `<div>Error: ${message}</div>`,
});HighGrid ships with a default role="grid" structure and basic arrow-key navigation between headers and cells.
8. Data Mutation API
grid.setRows(rows);
grid.appendRows([{ id: 3, name: "Carol" }]);
grid.updateRows([{ id: 1, name: "Alice Updated" }]);
grid.patchRow(1, { score: 1500 });
grid.upsertRows([
{ id: 2, score: 1111 },
{ id: 4, name: "Dave" },
]);
grid.removeRows([4]);9. Sorting, Filtering, Selection
grid.sortBy([{ field: "score", direction: "desc", type: "number" }]);
grid.clearSort();
grid.setQuickFilter("alice", ["name", "team"]);
grid.setColumnFilter("status", {
type: "text",
field: "status",
operator: "contains",
value: "Active",
});
grid.clearFilters();
grid.toggleSelectAll();
grid.setRowSelected(1, true);10. Grouping and Tree Data
grid.enableGrouping(["team"]);
grid.toggleGroup("team:Red");
grid.disableGrouping();
grid.enableTree({
treeMode: "children",
childrenField: "children",
hasChildrenField: "hasChildren",
onLoadChildren: async (row) => {
return [{ id: `${row.id}-1`, name: "Child", hasChildren: false }];
},
});
grid.expandAllTree();
grid.collapseAllTree();
grid.disableTree();11. Pagination and Infinite Scroll
Client mode:
grid.setDisplayMode("paginated");
grid.setPageSize(50);
grid.nextPage();
grid.enableInfiniteScroll();
grid.loadMoreInfinite();Server mode:
const grid = createGrid(container, {
rowKey: "id",
columns,
rows: [],
pagination: {
mode: "server",
pageSize: 25,
fetchPage: async ({ page, pageSize, filters, sort }) => {
const result = await fetchPageFromServer({
page,
pageSize,
filters,
sort,
});
return {
rows: result.rows,
totalCount: result.totalCount,
};
},
},
infiniteScroll: {
mode: "server",
initialLoadSize: 50,
loadMoreSize: 50,
onLoadMore: async ({ offset, loadSize, filters, sort }) => {
return await fetchMoreFromServer({ offset, loadSize, filters, sort });
},
},
});12. Live Updates
grid.liveAddRows([{ id: 1001, name: "Live Row" }]);
grid.liveUpdateRows([{ id: 1, name: "Updated in live mode" }]);
grid.livePatchRow(2, { status: "Review" });
grid.liveUpsertRows([{ id: 5, name: "Upserted" }]);
grid.liveRemoveRows([5]);
grid.pauseLiveUpdates();
grid.resumeLiveUpdates();
grid.setLiveMaxRows(2000);
grid.setLiveRowAnimationEnabled(true);
const stats = await grid.benchmarkLiveUpdates({
rowsPerSecond: 500,
durationMs: 3000,
});13. Cell Editing, Validation, and Clipboard
Add editable, parser, and validator to columns to enable the built-in editing flow.
const columns = [
{ id: "name", field: "name", headerName: "Name", editable: true },
{
id: "score",
field: "score",
headerName: "Score",
type: "number",
editable: true,
validator: ({ value }) => Number(value) >= 0 || "Score must be positive.",
},
];
grid.beginCellEdit(1, "name");
grid.setCellValue(1, "score", 120);
grid.validateRows();
const errors = grid.getValidationErrors();Clipboard helpers use tab-separated text so the output works well with spreadsheets.
grid.setRowSelected(1, true);
const text = grid.copySelectionToClipboard({
columns: ["name", "score"],
});
grid.pasteFromClipboard("Alice\t140", {
startRowKey: 1,
columns: ["name", "score"],
});14. Events
grid.on("render", (payload) => {
console.log("render", payload);
});
grid.on("row-click", ({ row, event }) => {
console.log("row click", row);
});
grid.on("cell-click", ({ row, colId, value }) => {
console.log("cell click", row, colId, value);
});
grid.on("cell-value-change", ({ rowKey, colId, value }) => {
console.log("cell value changed", rowKey, colId, value);
});
grid.on("selection-change", (payload) => {
console.log("selection changed", payload);
});
grid.on("state-change", ({ type }) => {
console.log("state changed", type);
});15. CSV/Excel Export and Context Menus
const csv = grid.exportCsv({
scope: "displayed",
columns: ["name", "team", "score"],
});
grid.downloadCsv({
scope: "all",
fileName: "operators.csv",
});
const excelHtml = grid.exportExcel({
scope: "all",
columns: ["name", "team", "score"],
});
grid.downloadExcel({
scope: "all",
fileName: "operators.xls",
});const grid = createGrid("#app", {
rowKey: "id",
columns,
rows,
onCellContextMenu: ({ row, colId, event }) => {
event.preventDefault();
console.log("context menu", row, colId);
},
});16. Plugins
Built-in plugins include uppercaseTeamPlugin and scorePrefixPlugin.
import {
createGrid,
uppercaseTeamPlugin,
createContextMenuPlugin,
createCsvShortcutPlugin,
} from "highgrid";
grid.usePlugin(uppercaseTeamPlugin);
grid.unusePlugin("uppercase-team");
grid.usePlugin(
createCsvShortcutPlugin({
fileName: "operators.csv",
}),
);
grid.usePlugin(
createContextMenuPlugin({
getItems: ({ row, core }) => [
{
label: `Export ${row.name}`,
onSelect: () => {
core.downloadCsv({ scope: "all", fileName: `${row.name}.csv` });
},
},
],
}),
);You can also create custom hook-based plugins.
const myPlugin = {
name: "my-plugin",
hooks: {
afterDataProcess(result) {
return {
...result,
displayRows: result.displayRows.map((row) => ({
...row,
name: `[Checked] ${row.name}`,
})),
};
},
},
};17. Persisting Column State
When tableId is provided, HighGrid can persist and restore column width, visibility, and pin state.
await grid.saveColumnState();
await grid.loadColumnState();
await grid.clearColumnState();18. Column Runtime API
grid.setColumnVisible("score", false);
grid.setColumnWidth("score", 160);
grid.setColumnPinned("name", "left");
grid.moveColumn("score", 2);
const allCols = grid.getAllLeafColumns();
const visibleCols = grid.getVisibleLeafColumns();19. Variable Row Height
const grid = createGrid(container, {
rowKey: "id",
columns,
rows,
variableRowHeight: true,
getRowHeight: (row) => (row.type === "detail" ? 80 : 40),
});20. Header Groups
const columns = [
{ id: "name", field: "name", headerName: "Name", width: 160 },
{
id: "location-group",
headerName: "Location",
children: [
{ id: "region", field: "region", headerName: "Region", width: 120 },
{ id: "country", field: "country", headerName: "Country", width: 100 },
],
},
];21. Vue 3 Adapter
npm install highgridComponent usage:
<template>
<HighGrid
:columns="columns"
:rows="rows"
row-key="id"
@row-click="onRowClick"
@selection-change="onSelectionChange"
/>
</template>
<script setup>
import { HighGrid } from "highgrid/vue";
</script>Composable usage:
<template>
<div ref="containerRef" style="height: 600px;" />
</template>
<script setup>
import { ref, onMounted } from "vue";
import { useHighGrid } from "highgrid/vue";
const containerRef = ref(null);
const { grid, state, init, setRows } = useHighGrid(containerRef, {
columns,
rows,
});
onMounted(() => init());
</script>22. Theming
Import tokens.css to expose every visual property as a CSS custom property.
import "highgrid/styles/tokens.css";
import "highgrid/styles/grid.css";Override any token on a parent element:
#my-grid {
--ag-accent: #7c3aed;
--ag-row-height: 48px;
--ag-font-size: 15px;
}Built-in theme presets:
<!-- Dark mode -->
<div class="ag-theme-dark"><div id="my-grid"></div></div>
<!-- Compact (32 px rows) -->
<div class="ag-theme-compact"><div id="my-grid"></div></div>
<!-- Spacious (52 px rows) -->
<div class="ag-theme-spacious"><div id="my-grid"></div></div>23. TypeScript
Type declarations are bundled — no separate @types package needed.
import { createGrid, GridOptions, ColumnDef } from "highgrid";
interface Row {
id: number;
name: string;
score: number;
}
const columns: ColumnDef<Row>[] = [
{ id: "name", field: "name", headerName: "Name" },
{ id: "score", field: "score", headerName: "Score", type: "number" },
];
const grid = createGrid<Row>(document.getElementById("app")!, {
rowKey: "id",
columns,
rows: [],
});24. State Query API
grid.getFilterState();
grid.getGroupingState();
grid.getTreeState();
grid.getColumnState();
grid.getSelectionState();
grid.getPaginationState();
grid.getRows(); // raw source rows
grid.getFlatRows(); // flattened display rows (includes group/tree nodes)25. Running the Example App
npm install
npm run devThe example app includes a floating control panel for display modes, live updates, column state, and quick benchmark actions.
26. GitHub Pages Deployment
GitHub Pages should serve the built demo site, not the library build entry from src/index.js.
npm run build:demoThis creates dist-pages/index.html. The .github/workflows/pages.yml workflow uploads that folder as the Pages artifact. For a repository Page, the default URL https://<user>.github.io/<repo>/ works without a custom domain.
A custom domain is optional. If you use one, configure it in GitHub Pages settings and point DNS to GitHub Pages. It does not fix the index.html location by itself, so the demo build is still required.
27. Before You Ship
npm testnpm run buildnpm run build:demo- manually verify scrolling, sorting, filtering, and live updates with real datasets
- validate response shapes for
fetchPage,onLoadMore, andonLoadChildrenwhen using server mode
28. React Adapter
Install HighGrid and import the React hook.
npm install highgridimport { useHighGrid } from "highgrid/react";
import "highgrid/styles/grid.css";
function MyGrid() {
const { containerRef, grid, state } = useHighGrid({
rowKey: "id",
columns,
rows,
rowHeight: 40,
});
return <div ref={containerRef} style={{ height: 600 }} />;
}useHighGrid returns:
| Property | Type | Description |
| -------------- | ------------------------ | ------------------------------------ |
| containerRef | RefObject | Attach to the container <div> |
| grid | GridCore \| null | Raw instance — available after mount |
| getGrid() | () => GridCore \| null | Stable ref accessor |
| isReady | boolean | true after the grid has mounted |
| state | object | Reactive selection + render summary |
state shape:
{
selectedKeys: Set<string>;
selectionCount: number;
isAllSelected: boolean;
isSomeSelected: boolean;
renderInfo: RenderPayload | null;
paginationState: PaginationState | null;
}The hook also exposes convenience delegates (setRows, appendRows, updateRows, patchRow, removeRows, setColumns, setQuickFilter, setColumnFilter, clearFilters, sortBy, nextPage, prevPage, usePlugin, liveAddRows, on, …) that forward directly to the underlying GridCore. Call getGrid() for any method not listed.
29. Custom Cell Editors
Import the built-in editor factories and assign them to editor on a column.
import {
createDateEditor,
createSelectEditor,
createTextareaEditor,
} from "highgrid";DateEditor — renders an <input type="date">:
{
id: "deadline",
field: "deadline",
headerName: "Deadline",
editable: true,
editor: createDateEditor,
editorOptions: { min: "2024-01-01", max: "2030-12-31" },
}SelectEditor — renders a <select> dropdown:
{
id: "status",
field: "status",
headerName: "Status",
editable: true,
editor: createSelectEditor,
options: ["Active", "Paused", "Review"],
// or with label/value pairs:
// options: [{ value: "A", label: "Active" }, { value: "P", label: "Paused" }]
// or a function: options: ({ row }) => getOptionsForRow(row)
}TextareaEditor — renders a <textarea> for long text:
{
id: "notes",
field: "notes",
headerName: "Notes",
editable: true,
editor: createTextareaEditor,
editorOptions: { rows: 4, placeholder: "Enter notes..." },
}Custom editor factory — the editor property accepts any function with signature ({ row, def, value }) => HTMLElement:
{
id: "score",
field: "score",
editable: true,
editor: ({ value }) => {
const input = document.createElement("input");
input.type = "number";
input.value = String(value ?? "");
input.className = "ag-cell-editor";
return input;
},
}30. Column Aggregation (Status Bar)
Enable the status bar and add aggregate to columns you want to summarise.
const grid = createGrid(container, {
rowKey: "id",
columns: [
{
id: "score",
field: "score",
headerName: "Score",
type: "number",
aggregate: "avg",
},
{
id: "revenue",
field: "revenue",
headerName: "Revenue",
type: "number",
aggregate: "sum",
},
],
rows,
statusBar: { enabled: true },
});Built-in aggregate values: "sum", "avg", "count", "min", "max".
Pass a function for custom aggregation:
{ id: "score", field: "score", aggregate: (values) => Math.median(values) }Control aggregation at runtime:
grid.setColumnAgg("score", "max");
grid.clearAggregates();31. Advanced Filter (AND/OR Tree)
setAdvancedFilter accepts a condition tree made of branch nodes (AND/OR) and leaf nodes.
grid.setAdvancedFilter({
type: "AND",
conditions: [
{
field: "status",
operator: "equals",
value: "Active",
filterType: "select",
},
{
field: "score",
operator: "greaterThan",
value: 5000,
filterType: "number",
},
{
type: "OR",
conditions: [
{ field: "region", operator: "equals", value: "Seoul" },
{ field: "region", operator: "equals", value: "Tokyo" },
],
},
],
});
grid.clearAdvancedFilter();Leaf node operators by filterType:
| filterType | operators |
| ---------------- | ---------------------------------------------------------------------------------------------------- |
| text (default) | contains, notContains, startsWith, endsWith, equals, notEquals |
| number | equals, notEquals, greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual, between |
| date | equals, notEquals, before, after, between |
| select | equals (matches single or array of values) |
32. Pivot Mode
grid.enablePivot({
rowFields: ["region"], // fields to use as row groups
columnField: "team", // field whose unique values become columns
valueField: "score", // numeric field to aggregate
aggFunction: "avg", // "sum" | "avg" | "count" | "min" | "max"
});
grid.disablePivot();Disable pivot and restore the original columns:
grid.disablePivot();
grid.setColumns(originalColumns);33. Row Pinning
Pin rows to the top or bottom of the grid — they stay visible while the body scrolls.
const firstRow = grid.getRows()[0];
grid.setPinnedTopRows([{ ...firstRow, name: "📌 " + firstRow.name }]);
grid.setPinnedBottomRows([{ id: "total", name: "Total", score: sumAll() }]);
// clear
grid.setPinnedTopRows([]);
grid.setPinnedBottomRows([]);34. Undo / Redo
Undo/redo tracks every cell edit automatically. Use keyboard shortcuts Ctrl+Z / Ctrl+Y (Windows) or Cmd+Z / Cmd+Shift+Z (Mac) when the grid is focused, or call the API directly.
if (grid.canUndo()) {
const action = grid.undo();
// action: { rowKey, colId, oldValue, newValue }
}
if (grid.canRedo()) {
const action = grid.redo();
}Configure the history depth at creation time:
const grid = createGrid(container, {
rowKey: "id",
columns,
rows,
undoRedoMaxHistory: 200, // default: 100
});35. Row Drag & Drop
Enable row dragging with rowDragging: true. A drag handle column is added automatically.
const grid = createGrid(container, {
rowKey: "id",
columns,
rows,
rowDragging: true,
onRowDrop: ({ fromRowKey, toRowKey }) => {
// reorder your data source here
},
});Listen to drag lifecycle events:
grid.on("row-drag-start", ({ rowKey }) => console.log("drag start", rowKey));
grid.on("row-drag-end", ({ rowKey }) => console.log("drag end", rowKey));36. Range Selection
Click and drag across cells to highlight a rectangular range. The selection highlights automatically. To read or clear the selection programmatically:
grid.clearRangeSelection();Listen to range changes:
grid.on("range-selection-change", ({ start, end }) => {
console.log("range", start, "→", end);
});37. Conditional Formatting
Add a conditionalFormat function to any column definition to apply per-cell styles or CSS classes.
{
id: "score",
field: "score",
headerName: "Score",
conditionalFormat: (value, row) => {
if (value >= 8000) return { style: { color: "#16a34a", fontWeight: "bold" } };
if (value <= 2000) return { style: { color: "#dc2626" } };
return null;
},
}The returned object may contain:
style—object— inline styles applied to the cell elementclass—string | string[]— CSS class names added to the cell element
Apply or remove conditional formatting at runtime with grid.setColumns(...):
const updated = grid.getAllLeafColumns().map((c) => ({
...c.def,
conditionalFormat: c.def.id === "score" ? myFormatFn : null,
}));
grid.setColumns(updated);38. Sparkline Plugin (Inline Charts)
The sparkline plugin renders a small SVG chart directly inside a cell. It is ideal when you want to compare trends across many rows at a glance.
import { createGrid, createSparklinePlugin } from "highgrid";
const grid = createGrid(container, {
columns: [
{ id: "id", field: "id", headerName: "ID" },
{ id: "name", field: "name", headerName: "Name" },
// sparkline columns — field must point to a number[]
{
id: "trend",
field: "history",
headerName: "Weekly Score · Line",
width: 140,
sparkline: { type: "line", field: "history", color: "#0f4c81" },
},
{
id: "trendBar",
field: "history",
headerName: "Weekly Score · Bar",
width: 140,
sparkline: { type: "bar", field: "history", color: "#7c3aed" },
},
],
rows,
plugins: [{ plugin: createSparklinePlugin() }],
});sparkline options per column:
| Option | Type | Default | Description |
| -------- | ------------------------------- | ----------------- | ------------------------------------------- |
| type | "line" \| "bar" \| "area" | "line" | Chart shape |
| field | string | column field | Row property containing the number[] data |
| color | string | "#0f4c81" | Stroke / fill colour |
| width | number | column width − 20 | SVG width in px |
| height | number | 28 | SVG height in px |
| align | "center" \| "left" \| "right" | "center" | Horizontal alignment inside the cell |
39. XLSX Export Plugin
The XLSX export plugin adds a downloadXlsx method to the grid. It uses ExcelJS — loaded from npm if available, otherwise falling back to a CDN.
import { createGrid, createXlsxExportPlugin } from "highgrid";
const grid = createGrid(container, {
columns,
rows,
plugins: [{ plugin: createXlsxExportPlugin({ fileName: "export.xlsx" }) }],
});
// trigger download
grid.downloadXlsx({ fileName: "my-data.xlsx" });
// or with scope
grid.downloadXlsx({ scope: "displayed", fileName: "filtered.xlsx" });If ExcelJS is not available, the plugin silently falls back to downloadExcel (HTML table format).
40. Status Bar
The status bar sits below the grid body and shows row count, filtered count, selected count, and column aggregates.
const grid = createGrid(container, {
rowKey: "id",
columns: [
{ id: "score", field: "score", headerName: "Score", aggregate: "avg" },
],
rows,
statusBar: { enabled: true },
});The bar updates automatically on every render. Locale keys for the status bar text:
const locale = {
grid: {
statusBar: {
totalRows: "{count} rows",
filteredRows: "{display} of {total} rows",
selectedRows: "{count} selected",
},
},
};41. Master-Detail (Expandable Rows)
Provide a detailRenderer to add expandable detail panels below each row.
const grid = createGrid(container, {
rowKey: "id",
columns,
rows,
masterDetail: {
detailRowHeight: 160,
detailRenderer: (row) => {
const div = document.createElement("div");
div.style.padding = "16px";
div.innerHTML = `<strong>${row.name}</strong><p>${row.notes}</p>`;
return div;
},
},
});
// toggle programmatically
grid.toggleDetail(rowKey);42. Print
grid.printGrid();printGrid calls window.print(). Style the printout with @media print CSS — HighGrid sets display: none on scrollbars and overlays automatically.
43. Multi-column Sorting (Shift+Click)
You can sort by multiple columns simultaneously. Hold Shift (or Ctrl/Cmd) and click on column headers to append them to the sorting criteria. Columns will show a sorting order priority number next to their directional arrows.
Or trigger it programmatically:
grid.sortBy([
{ field: "team", direction: "asc" },
{ field: "score", direction: "desc", type: "number" },
]);44. Column Header Dropdown Menu (Menu Button & Right-click)
Hovering over a column header reveals a menu icon button. Clicking this button or right-clicking anywhere on the header opens a context menu with options to sort, auto-size, or pin columns (Left, Right, or Unpin).
45. Column Auto-Sizing (autoSizeColumn / autoSizeAllColumns)
Automatically adjusts column widths based on cell content length using high-performance HTML5 Canvas measurement:
// Auto-size a specific column
grid.autoSizeColumn("name");
// Auto-size all columns
grid.autoSizeAllColumns();46. Inline Group Row Aggregations
When grouping rows, aggregate values (e.g. SUM, AVG) are evaluated and displayed inline directly within the group header row for columns configured with aggregations:
const grid = createGrid(container, {
rowKey: "id",
columns: [
{ id: "score", field: "score", headerName: "Score", aggregate: "sum" },
],
rows,
});
grid.enableGrouping(["team"]);47. 2D Rectangular Range Copy/Paste
Select a rectangular area of cells by clicking and dragging. Copy (Ctrl+C / Cmd+C) and paste (Ctrl+V / Cmd+V) data inside the grid. The format is fully compatible with Excel and Google Sheets (tab-delimited text).
Programmatic clipboard methods:
// Copy current range selection to tab-delimited text
const copiedText = grid.copySelectionToClipboard();
// Paste text into the grid starting from a specific cell
grid.pasteFromClipboard("Text1\tText2\nText3\tText4", {
startRowKey: "row-1",
columns: ["col-1", "col-2"],
});48. Functional Cell Edit Locking
Customize whether specific cells are editable based on row-level conditions by providing a function to editable in column definitions:
const columns = [
{
id: "name",
field: "name",
headerName: "Name",
// Prevent editing if the row status is locked
editable: ({ row, def }) => row.status !== "locked",
},
];49. Advanced Filter GUI Builder (Side Panel Filter Tab)
When the side panel is enabled, you can build complex filtering trees using multiple logical operators (AND/OR) and conditions in the GUI dialog under the Filters tab.
Programmatic configuration:
grid.setAdvancedFilter({
type: "AND",
conditions: [
{ field: "team", operator: "equals", value: "Red" },
{ field: "score", operator: "greaterThan", value: 1000 },
],
});50. Auto-numbering Row Column (Row number)
Display a sequential, auto-updating row number column. The numbers adjust automatically on sorting, filtering, and paging.
const grid = createGrid(container, {
rowNumbers: true, // Enable row numbers column
rowNumberWidth: 50, // Optional custom width (default: 44)
columns,
rows,
});51. Cell Flash Animation (Live Update Highlights)
When data changes dynamically via live mutation APIs (liveUpdateRows, livePatchRow, etc.), modified cells flash with a green highlight to visually call out the updates:
// Modified cells will flash automatically
grid.livePatchRow("row-1", { score: 9500 });52. Column Inline Filter Row
Render a dedicated row of text input fields directly below column headers for quick per-column text filtering:
const grid = createGrid(container, {
filterRow: { enabled: true }, // Enable inline filter row
columns,
rows,
});53. Keyboard Navigation Completed
Robust keyboard support makes grid interaction fully accessible:
Tab/Shift+Tab: Move focus to the next/previous cell. In edit mode, commits the edit and shifts focus.Enter: Focus cell and press Enter to edit. In edit mode, commits the value and shifts focus to the cell below.F2: Start editing the focused cell.Escape: Cancel editing/restore previous value, or clear range selection.Arrow keys/Home/End: Navigate focused cells. While editing, they natively move the text cursor within the input element.
54. Side Panel Drag & Drop Column Reordering
Reorder column visibility and sequence directly inside the settings panel. Drag and drop column list elements in the Columns tab of the side panel to reorder columns instantly in the viewport.
55. Fill Handle (Cell Autofill)
Drag the small square handle at the bottom-right of a focused cell to autofill values (duplicate or propagate) down or up adjacent cells:
const grid = createGrid(container, {
editing: { enabled: true },
fillHandle: true, // Enable fill handle
columns,
rows,
});56. Rich HTML Tooltip (Custom Popups)
Render custom HTML content tooltips when hovering over cells to show complex metadata, descriptions, or inline micro-charts:
const columns = [
{
id: "name",
field: "name",
tooltipComponent: ({ value, row, def }) => {
const el = document.createElement("div");
el.innerHTML = `<strong>${row.name}</strong><br/>Status: ${row.status}`;
return el; // Returns custom Element or string
},
},
];57. Multi-level Group Aggregations
HighGrid fully supports multi-level grouping (e.g. ['country', 'city', 'department']). Column aggregations are automatically calculated, aggregated, and displayed in group header rows at every nested level.
58. Web Worker Background Data Pipeline
Offloads CPU-heavy sorting and filtering tasks to a background thread to prevent UI freezing (jank) with large datasets. It uses a self-contained inline Web Worker dynamically generated inside the bundle to avoid path resolution issues:
const grid = createGrid(container, {
worker: { enabled: true }, // Enable background worker thread
columns,
rows,
});59. Basic Formulas (=SUM, =AVG)
Write Excel-like formulas beginning with = to calculate values dynamically. Includes single-cell references, A1:B10 range definitions, circular reference detection (#REF!), and memoized query caching. When editing, the cell editor automatically loads the raw formula string instead of the calculated result.
const rows = [
{ id: 1, a: 10, b: 20, c: "=SUM(A1:B1)" }, // Evaluates to 30
{ id: 2, a: 15, b: 25, c: "=AVG(A1:B2)" }, // Evaluates to 17.5
];