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

@helix-x/datagrid-ui

v0.1.2

Published

Dependency-free React data grid: virtualized rows, server-side paging/sorting/filtering, inline editing, Tailwind-styled

Readme

@helix-x/datagrid-ui

A dependency-free React data grid: virtualized rows, server-side paging / sorting / filtering, inline row editing, multi-select, CSV + clipboard export and persisted column preferences.

Its only runtime requirement is React. Styling is plain Tailwind utility classes, so there is no CSS file to import and no theme engine to configure.

Example

A runnable Vite gallery with a mock stock-market API lives in example/:

cd example
npm install
npm run dev

Four examples, all against a real server-side backend:

| | | | --- | --- | | Server-side market data | 480 symbols; paging, sorting and all four filter kinds resolved on the server, plus inline editing with server-side validation | | Images in rows | company logos and analyst avatars, lazy-loaded with reserved boxes and error fallbacks | | Live updates | an SSE feed patched in with api.updateRows() — no refetch, no lost scroll position | | Theme customization | accent, density and dark mode driven entirely by CSS custom properties |

Each example sits behind a Preview / Code toggle — the running grid, or its real source — and is followed by a How it works section pairing the explanation with the code that implements it, deep-linked to the exact lines on GitHub. Every snippet is extracted from the source files at build time, so none of it can drift.

Install

The package is a standalone module — it can be installed from a registry, a tarball, a git URL, or a relative path:

npm install @helix-x/datagrid-ui
# or, consuming it from a checkout next to your app:
npm install file:../helix-x-datagrid

React 18 or 19 must already be present; it is a peer dependency and is never bundled into the output.

Tailwind

The grid renders Tailwind utility classes. Tailwind only generates classes it can see, and it skips node_modules during automatic content detection, so point it at the shipped bundle explicitly:

/* your Tailwind v4 entry point */
@import "tailwindcss";
@source "../node_modules/@helix-x/datagrid-ui/dist/index.js";

On Tailwind v3, add the same path to content in tailwind.config.js, and define the four colour variables below as well. Row backgrounds (stripe, selection, editing, hover) are opaque colours mixed at runtime from var(--color-…), so the sticky checkbox column never shows the cells scrolling underneath it. v4 emits those variables for you; v3 emits none, and without them rows render with no background at all:

/* Tailwind v3 only */
:root {
  --color-white: theme(colors.white);
  --color-gray-50: theme(colors.gray.50);
  --color-gray-900: theme(colors.gray.900);
  --color-brand-500: theme(colors.brand.500);
}

Theme requirements

The grid uses the brand, gray and error colour families and the class-based dark variant. If your project does not already define them:

@custom-variant dark (&:is(.dark *));

@theme {
  --color-brand-25:  #f2f7ff;
  --color-brand-50:  #ecf3ff;
  --color-brand-200: #c2d6ff;
  --color-brand-400: #7592ff;
  --color-brand-500: #465fff;
  --color-brand-600: #3641f5;

  --color-error-50:  #fef3f2;
  --color-error-300: #fda29b;
  --color-error-500: #f04438;
  --color-error-600: #d92d20;
  --color-error-700: #b42318;
}

Usage

import { DataGrid, type ColumnDef, type GridApi } from '@helix-x/datagrid-ui';

const columns: ColumnDef<Person, Ctx>[] = [
  { field: 'id', header: 'ID', width: 90, filter: 'number' },
  { field: 'name', header: 'Name', flex: 1, filter: 'text', editable: true },
  { field: 'status', header: 'Status', filter: 'set',
    filterParams: { values: ['ACTIVE', 'CLOSED'] },
    editable: true, editor: 'select',
    editorParams: { options: [{ label: 'Active', value: 'ACTIVE' }] } },
];

<DataGrid
  columns={columns}
  getRowId={(row) => row.id}
  dataSource={{ getRows: (request, signal) => api.list(request, signal) }}
  storageKey="people"
  context={{ onEdit }}
  onRowCommit={async (draft) => {
    const parsed = schema.safeParse(draft);
    if (!parsed.success) return { ok: false, errors: toFieldErrors(parsed.error) };
    await api.save(draft);
    return { ok: true };
  }}
  apiRef={apiRef}
/>

Server contract

getRows receives a request that is structurally identical to ag-grid's server-side row model request, so an endpoint written for that model works unchanged:

{ startRow, endRow, sortModel: [{ colId, sort }], filterModel,
  rowGroupCols: [], valueCols: [], pivotCols: [], pivotMode: false, groupKeys: [] }

and returns { rows, lastRow }. Filter models use the same four shapes (text, number, date, set) that ag-grid emits.

Performance note

Column definitions should be memoised on []. Anything volatile — in-flight ids, permission checks, event handlers — belongs in context, which is passed to every cellRenderer. Changing context re-renders cells without rebuilding a single column definition.

API documentation

Full generated reference: https://suryakand.github.io/datagrid-ui/

Every exported symbol carries a doc comment — parameters, return values, defaults, and runnable examples on the types you actually write (ColumnDef, DataGridProps, HxDataSource, GridApi). The comments ship inside dist/index.d.ts too, so they appear on hover in any editor without visiting the site.

Build it locally:

npm run docs         # -> docs/
npm run docs:watch

The generator is TypeDoc, not JSDoc. It reads the same /** ... */ comments but takes parameter and return types from TypeScript itself, so signatures cannot drift from the code the way hand-written @param {Type} annotations do.

typedoc.json turns on link, export and coverage validation, and CI builds with --treatWarningsAsErrors. A broken {@link}, a type referenced from the public API but never exported, or a new export with no doc comment fails the docs build rather than shipping a gap.

Local development

npm install       # build toolchain only; React comes from the host app
npm run build     # dist/ — ESM, CJS and .d.ts
npm run dev       # rebuild on change
npm run typecheck
npm run docs      # docs/ — generated API reference

When an app consumes this package through a relative path or npm link, npm creates a symlink and Node can resolve a second copy of React from the package's own tree. Nothing here installs React (.npmrc sets legacy-peer-deps so the peer range is not auto-installed), but bundlers should still be told to dedupe:

// vite.config.ts
resolve: { dedupe: ['react', 'react-dom'] }

Releasing

Two workflows, deliberately separate:

| Workflow | Trigger | What it does | | --- | --- | --- | | .github/workflows/publish.yml | push to main | typecheck, build, write the release notes, publish to npm, tag v<version>, create the GitHub release with those notes, then commit the next version bump | | .github/workflows/docs.yml | the publish workflow completing successfully | rebuild the API reference at the released commit and deploy it to GitHub Pages |

The docs workflow keys off workflow_run rather than on: release. The publish job creates its release with the default GITHUB_TOKEN, and events raised by that token deliberately do not trigger further workflows — an on: release trigger would never fire. It also checks out the publish run's head_sha, so the documentation describes the code that was actually released rather than the version-bump commit pushed on top of it.

Release notes are built from the commits since the previous v* tag by .github/scripts/release-notes.mjs, because main is pushed to directly and GitHub's own generated notes only list merged pull requests. Commits written as Conventional Commits are grouped into breaking changes, features and fixes; any other message is listed as written, so a commit subject is the line users will read. Preview the next release's notes with:

node .github/scripts/release-notes.mjs

Repository settings this needs: Pages source set to GitHub Actions (Settings → Pages), and an NPM_TOKEN secret for the publish workflow.

Packaging

npm pack produces an installable tarball; prepack rebuilds dist/ first, so the published artifact is never stale.

Not in this version

Row grouping, pivoting, tree data, master/detail, variable row heights, and .xlsx output (CSV with a BOM opens natively in Excel).