Skip to main content
YK logo
← Back to projects

yk-grid — AI-Assisted React DataGrid Library

Building and publishing a production-ready React DataGrid component as an open-source alternative to expensive, overbuilt grid libraries

yk-grid React DataGrid component with AI query bar

Overview

yk-grid is a React DataGrid library I built from scratch and published to npm. It started as a frustrated side project. It ended up in production for a client build.

The library is a single generic component — DataGrid<T> — covering sorting, filtering, grouping, pagination, virtual scrolling, inline editing, CSV export, and an optional AI query bar that accepts natural language. It ships as dual ESM/CJS with full TypeScript generics. Runtime dependencies: React, Zod, and @tanstack/react-virtual.


The problem

Most React data grid libraries sit in one of two places: free but limited, or fully featured but expensive and overbuilt. AG Grid's community edition is fine until you need grouping or aggregation, at which point you're looking at an enterprise licence. TanStack Table is powerful but headless by design — you're still writing all the UI yourself. Neither option felt right for a team that needs a grid they can actually configure and ship without spending three days fighting the library first.

I'd used AG Grid across several client projects and kept building the same workarounds: custom renderers, patching around the enterprise paywall, working inside a library that had clearly grown to cover every use case rather than being designed for any specific one. At some point the frustration tipped over into: I wonder if I can just build this.


What I built

The library exposes a single entry point: DataGrid<T>. Column definitions are typed, row data flows in, and the grid handles the rest.

Grid state and row processing

All grid state — sorts, filters, grouping, pagination, selection, column widths, visibility — lives in a useReducer inside DataGrid.tsx. No context, no external store, no side effects in the reducer. Every action is a discriminated union in GridAction, which keeps state transitions easy to trace and testable in isolation without mounting anything.

Row processing runs as a useMemo: data flows through sort, then filter, then grouping, then pagination, each step a pure function in its own file. Display rows are a discriminated union (GroupHeaderRow | DataDisplayRow<T>), interspersed in the render loop so the grouping logic doesn't need special-casing throughout the tree.

Filtering

Column filter types are text, number, select, or date. Number columns get an operator picker (equals, greater than, less than, between). Select columns get a searchable checkbox list. Both the operator dropdown and the column visibility menu render via ReactDOM.createPortal to document.body using position: fixed coordinates from getBoundingClientRect() — the table container uses overflow: hidden and overflow-x: auto, so anything rendered inside it gets clipped. Every popup in the library follows the same pattern.

AI query bar

The AI bar is the part I'm most pleased with. You type something like "show failed refunds over £200, sorted by amount" and it translates that into grid actions — sorts, filters, grouping — via a Zod-validated JSON contract between the LLM response and the grid state.

The LLM response is validated against AiCommandSchema before it touches anything. The server handler (gridAiRoute.ts) is framework-agnostic — Express or Next.js App Router, no changes needed — and switches between Anthropic and OpenAI via an environment variable. The component's ai prop just takes an endpoint URL; the client never sees an API key. That last bit was the first piece of feedback I got: make sure this can't leak credentials. I'm glad it was raised early.

TypeScript generics

Getting DataGrid<T> to preserve its type parameter through forwardRef required a cast pattern. React's forwardRef drops generics, so without it consumers end up with DataGrid<unknown>. Getting the types right at the boundary mattered — a library that loses your types at the edge is only partially useful. GridRef<T> is exported for typed imperative access: getSelectedRows(), exportCsv(), setState().

Build and packaging

The build outputs ESM and CJS bundles via Vite, TypeScript declarations via tsc, and a separate server bundle so the AI route handler never enters the client bundle. React, Zod, and @tanstack/react-virtual are peer dependencies. Getting the package.json exports map right — yk-grid/server resolving to the server bundle, CSS importable, types resolving in both CJS and ESM contexts — took more iteration than I expected. The npm packaging docs are not great.


Challenges

Building a library is a different problem from building a product. When you're building a product, "does it work?" is usually enough. A library also has to be correct for consumers you'll never meet, in setups you haven't tested, with types that survive in their editor.

The trickiest implementation detail was the portal pattern. Portals work reliably in the browser, but JSDOM (used in Vitest) doesn't implement getBoundingClientRect, so any test checking portal coordinates gets zeroes. The tests work around it, but it's a known gap.

The TypeScript generics through forwardRef cost me real time early on. I didn't know that React.forwardRef drops type parameters, so I spent a while confused about why consumers were getting DataGrid<unknown>. Once I understood the cast pattern, the fix was small — but finding it wasn't.


Outcome

yk-grid is on npm at npmjs.com/package/yk-grid. My team adopted it for a production client build, which is the real test — the issues a demo app hides only show up when actual code depends on it.

The goal was never to go feature-for-feature with AG Grid. It was to build something that covers what most projects actually need, without the enterprise pricing or the weight. On that measure, it works.


Stack

Concern Technology
Framework React 19 · TypeScript
Build Vite · tsc
State useReducer (pure, no external store)
Validation Zod
Scrolling @tanstack/react-virtual
AI Anthropic Claude · OpenAI (switchable)
Testing Vitest · React Testing Library · Playwright
Styling CSS Modules · CSS custom properties
Publishing npm