@geovi/the-datagrid
v0.0.1
Published
A modern, feature-rich React data grid component built with shadcn/ui and TanStack Table
Maintainers
Readme
the-datagrid
A modern, feature-rich React data grid built on TanStack Table with a shadcn-aligned look-and-feel and self-contained packaged styles.
Documentation and live examples: https://geo-vi.github.io/the-datagrid/
Features
- Virtualized rendering for large datasets
- Sorting (single + multi-column)
- Filtering with a built-in filter row and operators
- Column management (reorder, resize, auto-size)
- Pagination (local + remote)
- Row selection (checkbox column)
- Modern shadcn-aligned look-and-feel with packaged styles
- Fully typed TypeScript API
- Migration-friendly API inspired by Inovua ReactDataGrid
Installation
npm install @geovi/the-datagrid react react-dom
# or
yarn add @geovi/the-datagrid react react-dom
# or
pnpm add @geovi/the-datagrid react react-domThat is the normal installation flow for consumers.
You do not need to install:
- Tailwind CSS
- shadcn/ui
- a separate package stylesheet import in normal app setups
This also applies when you use custom grid themes. The grid theme variables are plain CSS variables in the shipped stylesheet, so consumers do not need Tailwind just to use theme="dark", theme="hf-dark", or their own imported grid theme CSS.
Styling
@geovi/the-datagrid ships compiled CSS and loads it automatically from the package entry.
In a typical React app with a modern bundler, this is enough:
- Vite
- Next.js
- Webpack-based apps
- other setups that support CSS imports from npm packages
The grid keeps a shadcn-aligned look and will inherit app-level shadcn theme variables when they exist (--background, --foreground, --border, --ring, etc.). If they do not exist, the package uses scoped fallback tokens so it still renders correctly.
Theme variables are resolved at runtime from normal CSS, not from the consumer's Tailwind build. Tailwind is only used to build this library, not required to consume it.
Built-in theme behavior:
theme="default"follows the nearest.darkancestortheme="light"forces the light token settheme="dark"forces the dark token settheme="<custom-name>"activates a named custom theme on the grid root viadata-theme="<custom-name>"
If your environment does not process CSS imported from package entries, use the exported fallback once in your app:
import "@geovi/the-datagrid/style.css";Custom named themes
Named themes are configured with CSS variables on the grid root selector. For example, this custom hf-dark theme can be used with theme="hf-dark":
.tdg-root[data-theme="hf-dark"] {
--tdg-color-background: #191919;
--tdg-color-foreground: #e5e5e5;
--tdg-color-accent: #26324a;
--tdg-grid-bg: #191919;
--tdg-header-bg: #1e1e1e;
--tdg-grid-border-color: #2c2c2c;
--tdg-cell-border-color: #2c2c2c;
--tdg-header-border-color: #2c2c2c;
--tdg-row-odd-bg: #191919;
--tdg-row-even-bg: #191919;
--tdg-row-odd-hover-bg: #26324a;
--tdg-row-even-hover-bg: #26324a;
--tdg-row-selected-bg: #1a2740;
--tdg-row-selected-hover-bg: #1a2740;
--tdg-input-bg: #191919;
--tdg-input-border-color: #383838;
--tdg-input-border-color-hover: #383838;
--tdg-input-border-color-focus: #383838;
--tdg-select-bg: #191919;
--tdg-select-list-bg: #191919;
--tdg-select-border-color: #383838;
--tdg-select-border-color-hover: #383838;
--tdg-select-border-color-focus: #383838;
--tdg-select-item-hover-bg: #26324a;
--tdg-select-item-selected-bg: #26324a;
--tdg-checkbox-checked-bg: #688ad7;
--tdg-checkbox-checked-color: #e5e5e5;
--tdg-checkbox-indeterminate-bg: #688ad7;
color-scheme: dark;
}This lets you keep using a migration-friendly theme name such as hf-dark while mapping it to the grid’s internal theme tokens.
Basic usage
import { ReactDataGrid } from "@geovi/the-datagrid";
import type { TypeColumns } from "@geovi/the-datagrid";
export default function App() {
const columns: TypeColumns = [
{ name: "id", header: "ID", sortable: true, filterable: true },
{ name: "name", header: "Name", sortable: true, filterable: true },
{ name: "email", header: "Email", sortable: true, filterable: true },
];
const rows = [
{ id: 1, name: "John Doe", email: "[email protected]" },
{ id: 2, name: "Jane Smith", email: "[email protected]" },
];
return (
<ReactDataGrid
idProperty="id"
columns={columns}
dataSource={rows}
virtualized
enableFiltering
/>
);
}Advanced usage
import { useMemo, useState } from "react";
import { ReactDataGrid } from "@geovi/the-datagrid";
import type {
TypeColumns,
TypeFilterValue,
TypeI18n,
TypeOnSelectionChangeArg,
TypeRowSelection,
} from "@geovi/the-datagrid";
export default function App() {
const [selected, setSelected] = useState<TypeRowSelection>({});
const [columnOrder, setColumnOrder] = useState<string[]>([]);
const [filterValue, setFilterValue] = useState<TypeFilterValue | null>(null);
const columns: TypeColumns = useMemo(
() => [
{ name: "id", header: "ID", sortable: true, filterable: true },
{ name: "name", header: "Name", sortable: true, filterable: true },
{ name: "email", header: "Email", sortable: true, filterable: true },
],
[]
);
const rows = useMemo(
() => [
{ id: 1, name: "John Doe", email: "[email protected]" },
{ id: 2, name: "Jane Smith", email: "[email protected]" },
],
[]
);
const i18n: TypeI18n = useMemo(
() => ({
noRecords: "No records",
clear: "Clear",
contains: "Contains",
sortAsc: "Sort A→Z",
sortDesc: "Sort Z→A",
}),
[]
);
const onSelectionChange = (config: TypeOnSelectionChangeArg) => {
setSelected(config.selected);
};
return (
<ReactDataGrid
theme="default"
idProperty="id"
columns={columns}
columnOrder={columnOrder}
dataSource={rows}
enableFiltering
defaultFilterValue={filterValue ?? undefined}
onFilterValueChange={(v) => setFilterValue(v)}
filteredRowsCount={(count) => console.log("Filtered rows:", count)}
enableColumnFilterContextMenu
enableColumnAutosize
skipHeaderOnAutoSize={false}
onColumnOrderChange={setColumnOrder}
virtualized
columnUserSelect
i18n={i18n}
showColumnMenuTool={false}
checkboxColumn
onSelectionChange={onSelectionChange}
selected={selected}
/>
);
}onSelectionChange still emits the Inovua-style config object. For migration ergonomics, the grid also accepts the emitted object back through selected, so onSelectionChange={setSelectedRows} works with the same state variable used for selected.
Props (high-level)
Note: this is a curated overview. For the complete contract, refer to the exported TypeScript types.
Core
| Prop | Type | Default | Description |
| ------------ | ---------------- | ------------ | ------------------------------------------- |
| idProperty | string | required | Property name used as unique row identifier |
| columns | TypeColumns | required | Column definitions |
| dataSource | TypeDataSource | required | Data source (array, function, or promise) |
Display
| Prop | Type | Default | Description |
| ------------------ | ----------------------------- | ----------- | ------------------------------ |
| theme | string | "default" | Theme name |
| rowHeight | number | 44 | Row height in pixels |
| headerHeight | number | 40 | Header height in pixels |
| filterRowHeight | number | 44 | Filter row height in pixels |
| virtualized | boolean | true | Enable virtual scrolling |
| columnUserSelect | boolean \| "text" \| "none" | true | Column text selection behavior |
| showCellBorders | boolean \| "vertical" \| "horizontal" | true | Cell separator mode; use "horizontal" to disable vertical lines |
Columns
| Prop | Type | Default | Description |
| ---------------------- | --------------------------- | ------- | --------------------------------- |
| columnOrder | string[] | - | Ordered array of column ids/names |
| onColumnOrderChange | (order: string[]) => void | - | Fired when column order changes |
| enableColumnAutosize | boolean | true | Auto-calc column widths |
| skipHeaderOnAutoSize | boolean | false | Skip header when auto-sizing |
| showColumnMenuTool | boolean | false | Show column menu tool in header |
Filtering
| Prop | Type | Default | Description |
| ------------------------------- | ---------------------------------- | ------- | --------------------------------- |
| enableFiltering | boolean | true | Enable filter row |
| filterValue | TypeFilterValue | - | Controlled filter value |
| defaultFilterValue | TypeFilterValue | - | Uncontrolled initial filter value |
| onFilterValueChange | (value: TypeFilterValue) => void | - | Fired on filter change |
| enableColumnFilterContextMenu | boolean | false | Context menu for filter operators |
| filteredRowsCount | (count: number) => void | - | Reports filtered row count |
Sorting
| Prop | Type | Default | Description |
| ------------------------- | ------------------------------ | ------- | ----------------------------- |
| sortInfo | TypeSortInfo | - | Controlled sort state |
| defaultSortInfo | TypeSortInfo | - | Uncontrolled initial sort |
| onSortInfoChange | (info: TypeSortInfo) => void | - | Fired on sort change |
| allowUnsort | boolean | true | Allow returning to “unsorted” |
| defaultSortingDirection | "asc" \| "desc" | "asc" | Default sort direction |
Selection
| Prop | Type | Default | Description |
| ------------------- | -------------------------------------------- | ------- | ------------------------------ |
| checkboxColumn | boolean \| IColumn | false | Enable checkbox column |
| selected | TypeRowSelection | - | Controlled selection |
| defaultSelected | TypeRowSelection | - | Uncontrolled initial selection |
| onSelectionChange | (config: TypeOnSelectionChangeArg) => void | - | Fired on selection changes |
Pagination
| Prop | Type | Default | Description |
| --------------- | -------------------------------------- | ---------------------- | ---------------------------- |
| pagination | true \| false \| "remote" \| "local" | true | Pagination mode |
| skip | number | - | Controlled offset |
| defaultSkip | number | 0 | Initial offset |
| limit | number | - | Controlled page size |
| defaultLimit | number | 10 | Initial page size |
| onSkipChange | (skip: number) => void | - | Fired when offset changes |
| onLimitChange | (limit: number) => void | - | Fired when page size changes |
| pageSizes | number[] | [10, 20, 30, 40, 50] | Allowed page sizes |
Misc
| Prop | Type | Default | Description |
| ----------- | -------------------------- | ------- | ---------------------------------------- |
| i18n | TypeI18n | - | Text overrides (labels, operators, etc.) |
| loading | boolean | - | Loading state |
| onReady | (ref: RefObject) => void | - | Called when grid ref is ready |
| handle | (ref: RefObject) => void | - | Alias for onReady |
| className | string | - | Extra CSS classes |
| style | CSSProperties | - | Inline styles |
TypeScript
The package ships full type definitions.
import { ReactDataGrid } from "@geovi/the-datagrid";
import type {
TypeColumns,
TypeColumn,
TypeDataGridProps,
TypeRowSelection,
TypeOnSelectionChangeArg,
TypeFilterValue,
TypeSortInfo,
TypeI18n,
} from "@geovi/the-datagrid";