A lightweight, feature-rich, and highly customizable React Data Grid/Table component. Built for React 19 and Tailwind CSS v4, this component is designed with speed, responsiveness, type safety, and premium aesthetics in mind.
- β‘ Lightweight & Fast: Designed with minimal footprint and optimized with React memoization (
useMemo) to handle thousands of rows with no performance lag. βοΈ Smart Sorting: Natural alphanumeric and date-based sorting out of the box, with support for custom sort comparators.- π Multi-Level Filtering: Features global text search filtering and column-specific inputs.
- π Pagination: Highly configurable pagination controls supporting customizable page sizes, bounds, and ellipses.
- π± Responsive Layout: Adapts gracefully to smaller screens by optionally rendering rows as elegant mobile cards.
- π Dark & Light Modes: Native support for dark/light themes, with options to sync with system preferences (
auto) or force a specific mode. - π οΈ Flexible Column API: Render nested JSON paths (e.g.
user.profile.name) and write custom cell renderers for badges, actions, or interactive elements. - π Dual State Model: Use uncontrolled mode for immediate setup (internal state) or controlled mode for server-side sorting, searching, and paging.
- π¨ Tailwind Customization: Easily pass tailwind override classes to headers, cells, wrappers, or buttons via the
classNamesproperty.
Install via npm, yarn, or pnpm:
# npm
npm install @ohryzon/react-tablez
# yarn
yarn add @ohryzon/react-tablez
# pnpm
pnpm add @ohryzon/react-tablezDefine your columns and data array, and mount the component:
import { Table, Column } from '@ohryzon/react-tablez';
interface User {
id: number;
name: string;
role: string;
status: string;
}
const columns: Column<User>[] = [
{ key: 'name', header: 'Name', sortable: true, filterable: true },
{ key: 'role', header: 'Role', sortable: true },
{
key: 'status',
header: 'Status',
render: (val) => (
<span className={val === 'Active' ? 'text-green-500' : 'text-gray-500'}>
β {val}
</span>
)
}
];
const data: User[] = [
{ id: 1, name: 'Alice Smith', role: 'Developer', status: 'Active' },
{ id: 2, name: 'Bob Johnson', role: 'Designer', status: 'Inactive' }
];
export default function Dashboard() {
return (
<Table<User>
data={data}
columns={columns}
pagination={true}
defaultPageSize={5}
/>
);
}Below is an example showcasing advanced capabilities like nested JSON path keys, row actions, and theme toggle selectors:
import React, { useState } from 'react';
import { Table, Column, RowAction } from '@ohryzon/react-tablez';
interface Employee {
id: number;
name: string;
profile: {
title: string;
salary: number;
};
status: 'Active' | 'Inactive';
}
const columns: Column<Employee>[] = [
{ key: 'name', header: 'Employee Name', sortable: true },
// Deep nested JSON property
{ key: 'profile.title', header: 'Position', sortable: true, filterable: true },
{
key: 'profile.salary',
header: 'Annual Salary',
sortable: true,
dataType: 'number',
render: (val) => `$${val.toLocaleString()}`
},
{
key: 'status',
header: 'Status',
render: (val) => (
<span className={`px-2.5 py-0.5 rounded-full text-xs font-semibold ${val === 'Active' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}`}>
{val}
</span>
)
}
];
export default function StaffDirectory() {
const [staff, setStaff] = useState<Employee[]>([
{ id: 1, name: 'Sarah Connor', profile: { title: 'Engineering Lead', salary: 125000 }, status: 'Active' },
{ id: 2, name: 'John Miller', profile: { title: 'Senior Developer', salary: 98000 }, status: 'Active' }
]);
const actions: RowAction<Employee>[] = [
{
name: 'edit',
label: 'Edit',
onClick: (row) => console.log('Editing row:', row)
},
{
name: 'delete',
label: 'Delete',
onClick: (row) => setStaff((prev) => prev.filter((item) => item.id !== row.id)),
className: 'text-red-600 hover:text-red-850'
}
];
return (
<Table<Employee>
data={staff}
columns={columns}
actions={actions}
actionsLayout="dropdown" // Renders action choices as a dropdown menu
theme="dark" // Supports 'light', 'dark', 'auto'
pagination={true}
defaultPageSize={10}
columnFilters={true} // Column specific filter inputs
/>
);
}To manage pagination, sorting, and filtering externally (e.g. for server-side SQL queries or API pagination), pass the controlled* properties:
<Table<User>
data={apiData}
columns={columns}
// Controlled Pagination
controlledPagination={{
currentPage: page,
pageSize: pageSize,
totalItems: totalRowsOnServer,
onPageChange: (newPage) => setPage(newPage),
onPageSizeChange: (newSize) => setPageSize(newSize)
}}
// Controlled Sorting
controlledSorting={{
sortBy: activeSortField,
sortOrder: activeSortDirection, // 'asc' | 'desc' | null
onSort: (field, order) => {
setActiveSortField(field);
setActiveSortDirection(order);
}
}}
// Controlled Filtering
controlledFiltering={{
globalFilter: searchQuery,
onGlobalFilterChange: (query) => setSearchQuery(query)
}}
/>| Property | Type | Default | Description |
|---|---|---|---|
data |
T[] |
Required | Array of objects representing the row data. |
columns |
Column<T>[] |
Required | Array of column definitions. |
rowKey |
keyof T | ((row: T, index: number) => string | number) |
undefined |
Unique key for each row. |
loading |
boolean |
false |
When true, renders an animated skeleton loading state. |
loadingElement |
React.ReactNode |
undefined |
Custom React skeleton loader to override default. |
emptyState |
React.ReactNode |
undefined |
Custom element to render when the table is empty. |
actions |
RowAction<T>[] |
undefined |
Custom row operations (e.g. edit, delete actions). |
actionsHeader |
string |
"Actions" |
Custom header label for the Actions column. |
actionsLayout |
"inline" | "dropdown" |
"inline" |
Whether to display buttons next to each other or inside a menu. |
theme |
"light" | "dark" | "auto" |
"light" |
Layout palette theme. "auto" syncs with system media preference. |
responsiveCards |
boolean |
true |
When true, collapses rows into cards on screen sizes under 768px. |
classNames |
TableClassNames |
{} |
Custom Tailwind classes to override individual table tags. |
pagination |
boolean |
true |
Enable client-side pagination. |
defaultPageSize |
number |
10 |
Default number of records per page. |
pageSizeOptions |
number[] |
[5, 10, 20, 50] |
Dropdown page size selections. |
globalFilter |
boolean |
true |
Enable global searching input. |
columnFilters |
boolean |
false |
Enable input fields for individual columns. |
| Option | Type | Default | Description |
|---|---|---|---|
key |
string |
Required | Access key of the row object. Supports nested dot notation (e.g. details.address.zip). |
header |
React.ReactNode |
Required | Header label or React node. |
sortable |
boolean |
false |
Allow sorting by clicking the header. |
filterable |
boolean |
false |
Enable text input filtering for this column. |
dataType |
"string" | "number" | "date" | "boolean" |
"string" |
Data type used for logical alphanumeric or chronological sorting. |
render |
(value: any, row: T, index: number) => React.ReactNode |
undefined |
Custom renderer callback for cells in this column. |
sortType |
(a: any, b: any) => number |
undefined |
Custom sorting comparator. |
headerClassName |
string |
"" |
Custom Tailwind classes to style this header cell. |
cellClassName |
string |
"" |
Custom Tailwind classes to style these cells. |
Inject custom styles into the component using the classNames option. Available selectors:
export interface TableClassNames {
wrapper?: string; // Main container block
table?: string; // Table HTML element
thead?: string; // Table head element
th?: string; // Table header cell element
tbody?: string; // Table body element
trRow?: string; // Body row element
td?: string; // Body cell element
pagination?: string; // Footer block wrapper
paginationButton?: string; // Page buttons
globalFilterWrapper?: string; // Search container
globalFilterInput?: string; // Search input text field
columnFiltersWrapper?: string; // Column filter row
columnFilterInput?: string; // Column filter input field
actionsWrapper?: string; // Actions block container
actionsButton?: string; // Actions buttons
}Inside the project directory, you can run the following:
- Run Dev Server (for viewing the dashboard demo):
npm run dev
- Execute Unit Test Suite (via Vitest & Testing Library):
npm run test - Build Demo Application:
npm run build
- Package the Library for NPM (generates UMD/ESM files and TS declarations in
dist/):npm run build:lib
- Compile the library assets and declaration files:
npm run build:lib
- Log in to your npm registry profile:
npm login
- Publish the compiled
dist/package (pass--access publicas it is a scoped package):npm publish --access public
Dedicated to the public domain under the Unlicense. Feel free to use, copy, modify, or distribute.