npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jh-grid/jhgrid-js

v0.1.0

Published

High-performance Canvas-based data grid with smooth 2D virtualization.

Readme

JH Grid

High-performance Canvas-based data grid with smooth 2D virtualization.
Renders millions of rows and columns with near-zero DOM overhead.

JH Grid screenshot

This repository distributes the pre-built bundle (jhgrid.esm.js / jhgrid.js / jhgrid.min.js) plus its documentation, not the buildable source tree. See docs/ 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/max compare chronologically on a type: 'date' column)
  • In-cell action buttons: type: 'button' columns render a clickable pill per row (e.g. "Delete", "Approve") independent of editableCols
  • 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 pluggable CellEditors/CellRenderers registry (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 matching getNew*()/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 explicit headerRows) merges header cells across levels
  • Per-row / per-cell styling callbacks: rowHighlighter / cellBackground for conditional formatting
  • State snapshot/restore: getState() / setState() for saving and restoring grid state (filters, sort, column order/visibility, edits)
  • Built-in localization: locale option with bundled KO_I18N / JA_I18N / ZH_I18N text packs, per-key i18n overrides, 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/:


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.