@jh-grid/jhgrid-js
v0.1.0
Published
High-performance Canvas-based data grid with smooth 2D virtualization.
Maintainers
Readme
JH Grid
High-performance Canvas-based data grid with smooth 2D virtualization.
Renders millions of rows and columns with near-zero DOM overhead.

This repository distributes the pre-built bundle (
jhgrid.esm.js/jhgrid.js/jhgrid.min.js) plus its documentation, not the buildable source tree. Seedocs/for the full reference.
Features
- Smooth rendering via
requestAnimationFrame+ Canvas 2D, synced to your display's native refresh rate (60Hz, 120Hz, 144Hz, etc.), not capped at 60fps - 2D virtual scrolling: only visible cells are drawn
- Chunk-based async data loading with prefetch & cache
- HiDPI / Retina display support (devicePixelRatio scaling)
- Draggable scrollbars (vertical + horizontal)
- Left/right frozen columns (
frozenCols/frozenColsRight) - Cell click: single cell selection with blue border highlight
- Cell drag: multi-cell range selection with fill overlay
- Row selection: single/multi row selection (
rowSelection: 'single' | 'multi'), with an optional select-all header checkbox (columnDefs[].headerCheckbox) - Row drag reorder: drag rows by the row-number gutter (
rowReorder: true) - Double-click to edit: per-column editable/readonly control
- Ctrl+C / Ctrl+V: copy & paste (single cell or range, TSV format)
- Ctrl+Z / Ctrl+Y: undo / redo (cell edits, row/column add/delete)
- Column validation: declarative required/pattern/min/max/length/custom rules with red-border + tooltip error display (
min/maxcompare chronologically on atype: 'date'column) - In-cell action buttons:
type: 'button'columns render a clickable pill per row (e.g. "Delete", "Approve") independent ofeditableCols - Date / rich-text / image cell types:
type: 'date'opens a native date/datetime picker;type: 'richtext'opens an inline bold/italic/underline/strikethrough editor;type: 'image'renders a cell image (fit: 'cover' | 'contain', size-aware decoding, shared LRU cache), plus a pluggableCellEditors/CellRenderersregistry (registerCellEditor()/registerCellRenderer()) for fully custom editors and renderers - Set filter: checkbox list of a column's distinct values in the header filter panel (
setFilterValues()) - Quick filter: global cross-column search term (
setQuickFilter()/getQuickFilter()/clearQuickFilter()) - Single-column sort:
setSort()/removeSort()/clearSort() - Row / column CRUD:
addRow()/deleteRow()/undeleteRow(),addColumn()/deleteColumn()/undeleteColumn(), with matchinggetNew*()/getDeleted*()accessors for diff-based saves - Column hide/show:
hideColumn()/showColumn()/isColumnVisible()/getHiddenColumns(), plus per-row/column resize (setRowHeight(),autoFitColumns()) - Multi-level header groups:
columnDefs[].group(or explicitheaderRows) merges header cells across levels - Per-row / per-cell styling callbacks:
rowHighlighter/cellBackgroundfor conditional formatting - State snapshot/restore:
getState()/setState()for saving and restoring grid state (filters, sort, column order/visibility, edits) - Built-in localization:
localeoption with bundledKO_I18N/JA_I18N/ZH_I18Ntext packs, per-keyi18noverrides, and locale-aware number/date/currency cell rendering - Accessibility: ARIA labeling, keyboard-navigable header/row focus, and automatic high-contrast (
forced-colors) theme remapping - CSV export + print preview:
exportCsv(),printGrid() - Arrow key navigation: keyboard-driven cell movement
- Enter / Tab: commit edit and move to next row / column
- Text overflow with ellipsis (
…): O(log n) binary search - Fully themeable
- Zero dependencies
Installation
npm package coming soon. For now, use one of the two options below.
Option A: Static ES module (no bundler)
jhgrid.esm.js is a single self-contained ES module file: deploy it as-is as a static resource
(e.g. from a Spring Boot static resource path) and import it directly, no build step required:
<script type="module">
import { JHGrid } from '/static/jhgrid.esm.js';
</script>Option B: CDN (single bundled script)
jhgrid.min.js is an IIFE build served straight from this repository via jsDelivr, no npm
install required. Everything is exposed on a single global, JHGrid (the grid constructor is
JHGrid.JHGrid):
<!-- pin an exact tag/commit for production; @latest always serves the latest commit on that branch -->
<script src="https://cdn.jsdelivr.net/gh/JH-Grid/JHGrid@latest/dist/jhgrid.min.js"></script>
<script>
const grid = new JHGrid.JHGrid({
container: '#my-grid',
// ...
});
</script>Quick Start
Data already in memory (default)
Pass an array you already have (an API response, a small/medium table) straight in via data,
no fetch functions needed:
<div id="my-grid"></div>
<script type="module">
import { JHGrid } from './dist/jhgrid.esm.js'; // adjust to wherever you host the file
const grid = new JHGrid({
container: '#my-grid',
width: 1200,
height: 700,
data: [
{ name: 'Alice', age: 30, city: 'Seoul' },
{ name: 'Bob', age: 25, city: 'Busan' },
],
// Columns allowed to be edited (all columns are readonly if omitted)
editableCols: '*', // or ['name', 'age'] for a subset
// Called whenever a cell value changes. oldValue is the prior edit if the cell was already
// dirty, otherwise the row's original (pre-edit) value.
onCellChange: ({ row, field, newValue, oldValue }) => {
console.log(`[${row}] ${field}: ${oldValue} -> ${newValue}`);
},
});
</script>See Local Array Data for how filtering/sorting/refresh()
behave against a plain array.
Server-paginated data
For a large dataset that shouldn't be loaded into memory all at once, fetch it page by page instead:
<div id="my-grid"></div>
<script type="module">
import { JHGrid } from './dist/jhgrid.esm.js'; // adjust to wherever you host the file
const grid = new JHGrid({
container: '#my-grid',
width: 1200,
height: 700,
editableCols: '*',
fetchMeta: async () => {
const res = await fetch('/api/grid/meta');
return res.json(); // { totalRows: number, columns: string[] }
},
fetchData: async (page, size) => {
const res = await fetch(`/api/grid/data?page=${page}&size=${size}`);
return res.json(); // { rows: object[] }
},
});
</script>See fetchPage for a single-callback
alternative to fetchMeta+fetchData when your backend already returns both together.
Documentation
Browse the docs online, or read them directly in
docs/:
- API Reference: every constructor option, the data source interface, pagination, and all public methods
- Theming: the full theme object, styling with your own CSS via
--jhg-*custom properties, and canvas motion tuning - Interaction Reference: every mouse and keyboard interaction
- Spring Boot Integration: backend API shape and SQL pagination
- Architecture: internal structure and the virtual rendering flow
- Browser Support: minimum versions and what sets them
Browser Support
Chrome/Edge 92+, Firefox 90+, Safari 15.4+: no IE, no legacy Edge, no transpilation or polyfills. See docs/browser-support.md for exactly what sets that floor.
License
Proprietary: free to use in your own applications, no redistribution or reverse engineering of the compiled bundle. See LICENSE and NOTICE.
