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

@algodomain/smart-datatable

v2.0.0

Published

This guide explains how to use the DataTable component: core concepts, feature set, and real-world examples for client, server, and auto (hybrid) modes. It also covers filtering, sorting, pagination, selection, column management with comprehensive reset f

Readme

@algodomain/smart-datatable

This guide explains how to use the DataTable component: core concepts, feature set, and real-world examples for client, server, and auto (hybrid) modes. It also covers filtering, sorting, pagination, selection, column management with comprehensive reset functionality, localStorage persistence, and backend integration via ORM adapters.


⚠️ Breaking Changes in v2.0.0

If upgrading from v1.x, please note:

  • API properties: The DataTable uses a single crudEndpoint (singular) for all CRUD operations—GET, POST, PUT, DELETE. There is no crudEndpoints (plural) or separate endpoints per operation.
  • CRUD setup: Add/Edit/Delete with SmartForms requires crudEndpoint, one column with isPrimaryKey: true, and peer dependencies @algodomain/smart-forms, zod, react-toastify, date-fns. See CRUD (Add/Edit/Delete) for full setup.
  • Review the Complete Properties Reference and API Endpoints Configuration for current prop names and usage.

Table of Contents

🆕 What's New

Documentation

Reference


Package Details

Install and Configure

Requirements:

  • React 18+
  • Tailwind CSS 4.x+
  1. Install the package (choose your manager):
pnpm add @algodomain/smart-datatable
# or
npm i @algodomain/smart-datatable
# or
yarn add @algodomain/smart-datatable
  1. Make sure your Tailwind CSS entry file (e.g., src/index.css) sources the DataTable package so Tailwind scans its class names. Add ONE of the following @source lines that exists in your node_modules:
/* Ensure Tailwind scans DataTable package classes */
@source "../node_modules/@algodomain/smart-datatable/dist/index.js";
/* OR */
@source "../node_modules/@algodomain/smart-datatable/dist/index.cjs";

If your index.css uses Tailwind v4 style imports, you might already have:

@import "tailwindcss";
  1. Import the DataTable styles once in your app (e.g., in src/main.tsx or on the page where you use the table):
import "@algodomain/smart-datatable/style.css";
  1. Use the component:
import { DataTable } from "@algodomain/smart-datatable";
import type { ColumnDef, AuthConfig } from "@algodomain/smart-datatable";

Basics

  • The DataTable is a React + TypeScript component styled with Tailwind and shadcn/ui.
  • Core component import:
import { DataTable } from "@algodomain/smart-datatable"
import type { ColumnDef } from "@algodomain/smart-datatable"

Defining Columns

Each column describes how a field is rendered, sorted, and filtered.

interface Task {
  id: string
  title: string
  status: "In-Progress" | "Canceled" | "Completed" | "Pending" | "New"
  priority: "High" | "Medium" | "Low"
  estimatedHours: number
  createdAt: Date | string
  assignee: string
}

const columns: ColumnDef<Task>[] = [
  { id: "id", header: "Task ID", accessorKey: "id", width: 100, sortable: true, filterable: true, dataType: "string", filterType: "text" },
  { id: "title", header: "Title", accessorKey: "title", width: 200, sortable: true, filterable: true, dataType: "string", textWrap: true },
  {
    id: "status",
    header: "Status",
    accessorKey: "status",
    width: 120,
    sortable: true,
    filterable: true,
    dataType: "string",
    filterType: "select",
    filterOptions: [
      { label: "New", value: "New" },
      { label: "In-Progress", value: "In-Progress" },
      { label: "Pending", value: "Pending" },
      { label: "Completed", value: "Completed" },
      { label: "Canceled", value: "Canceled" },
    ],
  },
  { id: "estimatedHours", header: "Est. Hours", accessorKey: "estimatedHours", width: 100, sortable: true, filterable: true, dataType: "number", filterType: "number" },
  { id: "createdAt", header: "Created At", accessorKey: "createdAt", width: 180, sortable: true, filterable: true, dataType: "date", filterType: "date" },
]

Notes:

  • dataType improves filtering and sorting behavior.
  • filterType: "select" requires filterOptions.
  • textWrap controls whether cell text wraps within column width.

Modes: Client, Server, Auto

  • Client (in-memory): Use when you already have data on the client, or want to fetch all data once from an API endpoint and handle filtering/sorting/pagination client-side.
  • Server: Data is fetched via an API endpoint; sorting/filtering/pagination are handled server-side.
  • Auto (hybrid): Smart caching; starts server-side and can switch to client when all records are cached.

Client Mode Examples

Client Mode with Data Prop:

<DataTable
  columns={columns}
  data={myTasks} // array of Task
  mode="client"
  // Table identification & persistence
  tableId="tasks-table"
  enablePersistence={true}
  persistFilters={true}
  persistSorting={true}
  persistColumnSettings={true}
  // UX
  enableSelection
  selectionMode="multiple"
  enableFiltering
  enableGlobalSearch
  enableSorting
  enableMultiSort
  enableColumnVisibility
  enableColumnReorder
  enableColumnResize
  enableColumnFreeze
  pageSizeOptions={[5, 10, 20, 50]}
  initialPageSize={10}
  showColumnBorders
  onRowClick={(row) => console.log("Row:", row)}
  onSelectionChange={(rows) => console.log("Selected:", rows)}
/>

Client Mode with API Endpoint:

<DataTable
  columns={columns}
  crudEndpoint="http://localhost:7070/task-details"
  mode="client"
  // Table identification & persistence
  tableId="api-tasks-table"
  enablePersistence={true}
  persistFilters={true}
  persistSorting={true}
  persistColumnSettings={true}
  // UX
  enableSelection
  selectionMode="multiple"
  enableFiltering
  enableGlobalSearch
  enableSorting
  enableMultiSort
  enableColumnVisibility
  enableColumnReorder
  enableColumnResize
  enableColumnFreeze
  pageSizeOptions={[5, 10, 20, 50]}
  initialPageSize={10}
  showColumnBorders
  onRowClick={(row) => console.log("Row:", row)}
  onSelectionChange={(rows) => console.log("Selected:", rows)}
  onError={(error) => console.error("API Error:", error)}
/>

Client Mode Validation:

  • ✅ Use data prop: All data provided directly
  • ✅ Use crudEndpoint prop: Fetches all data once via GET, then handles everything client-side
  • Error: Cannot use both data and crudEndpoint in client mode - the table will display a validation error

API Response Format for Client Mode: The API endpoint should return either:

  • An array of objects directly: [{...}, {...}]
  • An object with a data property: { data: [{...}, {...}] }

HTTP Method Requirements:

  • Client Mode: Requires GET endpoint (fetches all data once)
  • Server Mode: Requires POST endpoint (sends pagination/filtering requests)
  • Auto Mode: Requires POST endpoint (sends pagination/filtering requests)

API Endpoints Configuration:

The DataTable uses two distinct endpoint properties:

| Property | Type | Usage | HTTP Methods | Required When | |----------|------|-------|--------------|---------------| | crudEndpoint | string | Single base URL for CRUD operations | GET (load), POST (create), PUT /:id (update), DELETE /:id (delete) | API-backed data; Add/Edit/Delete when enableAddRecord / enableEditRecord / enableDeleteRecord | | searchAndFilterEndpoint | string | POST endpoint for paginated search/filter/sort | POST only | mode="server" or mode="auto" |

Important: There is only one crudEndpoint (singular), not multiple endpoints. All CRUD operations use this base URL:

  • Client mode: GET crudEndpoint fetches all data once; Add/Edit/Delete use POST, PUT, DELETE on the same base.
  • Server/Auto mode: searchAndFilterEndpoint handles search; crudEndpoint handles Add/Edit/Delete.

Auth configuration (optional, applies to all API requests):

interface AuthConfig {
  accessTokenKey?: string;   // localStorage key (default: 'accessToken')
  refreshTokenKey?: string;  // localStorage key for refresh token
  refreshUrl?: string;       // URL to call on 401/403 for token refresh
}

API Configuration Examples:

// Client mode - crudEndpoint for GET (load) and CRUD
<DataTable
  columns={columns}
  crudEndpoint="/api/tasks"
  mode="client"
/>

// Server mode - searchAndFilterEndpoint for POST search, crudEndpoint for CRUD
<DataTable
  columns={columns}
  crudEndpoint="/api/tasks"
  searchAndFilterEndpoint="/api/tasks/search"
  auth={{
    accessTokenKey: "accessToken",
    refreshTokenKey: "refreshToken",
    refreshUrl: "/api/auth/refresh"
  }}
  mode="server"
/>

Benefits of Client Mode with API Endpoint:

  • 🚀 Single API Call: Fetches all data once, then handles filtering/sorting/pagination client-side
  • Fast Interactions: No network delays for filtering, sorting, or pagination after initial load
  • 💾 Offline Capable: Works offline after initial data fetch
  • 🔄 Consistent UX: Same user experience as traditional client-side tables
  • 📊 Perfect for Medium Datasets: Ideal when you have 100-10,000 records that can fit in memory

Server Mode Example

<DataTable
  columns={columns}
  crudEndpoint="http://localhost:7070/task-details"
  searchAndFilterEndpoint="http://localhost:7070/task-details"
  mode="server"
  backendORM="prisma" // prisma | mongoose | typeorm | custom
  // Table identification & persistence
  tableId="server-tasks-table"
  enablePersistence={true}
  persistFilters={true}
  persistSorting={true}
  persistColumnSettings={true}
  pageSizeOptions={[5, 10, 25, 50]}
  initialPageSize={10}
  enableFiltering
  enableGlobalSearch
  enableSorting
  enableMultiSort
  enableColumnVisibility
  enableColumnReorder
  enableColumnResize
  enableColumnFreeze
  maxHeight="70vh"
  showColumnBorders
  onError={(e) => console.error(e)}
/>

Expected request payload shape (client → server):

{
  "page": 1,
  "pageSize": 25,
  "sort": [
    { "column": "createdAt", "direction": "desc" },
    { "column": "priority", "direction": "asc" }
  ],
  "filters": [
    { "column": "status", "operator": "=", "value": "In-Progress" },
    { "column": "estimatedHours", "operator": ">=", "value": 10 }
  ],
  "logicalOperator": "AND"
}

Expected server response:

{
  "data": [ /* array of rows */ ],
  "totalRecords": 1250,
  "page": 1,
  "pageSize": 25,
  "totalPages": 50
}

Auto (Hybrid) Mode Example

<DataTable
  columns={columns}
  crudEndpoint="http://localhost:7070/task-details"
  searchAndFilterEndpoint="http://localhost:7070/task-details"
  mode="auto"
  // Table identification & persistence
  tableId="auto-tasks-table"
  enablePersistence={true}
  persistFilters={true}
  persistSorting={true}
  persistColumnSettings={true}
  serverPageSize={10}
  pageSizeOptions={[5, 10, 25, 50]}
  initialPageSize={5}
  enableSelection
  selectionMode="multiple"
  enableFiltering
  enableGlobalSearch
  enableSorting
  enableMultiSort
  enableColumnVisibility
  enableColumnReorder
  enableColumnResize
  enableColumnFreeze
  maxHeight="70vh"
  showColumnBorders
/>

Hybrid behavior highlights:

  • Caches fetched server pages; won’t re-fetch pages already cached.
  • If all records become cached, operations switch to client mode automatically.

Filtering

There are two per-column filtering UIs, selected automatically:

  • Select filter (when a column has filterType: "select" with filterOptions).
  • Advanced filter for text/number/date with multiple conditions and logical operators.

Operators by type:

  • String: =, !=, contains, startsWith, endsWith, isEmpty, isNotEmpty
  • Number/Date: all string operators plus >, <, >=, <=, between

Examples:

// Text contains
onFilter({ column: "title", operator: "contains", value: "auth" })

// Number between
onFilter({ column: "estimatedHours", operator: "between", value: [10, 20] })

// Select with multiple values
onFilter({ column: "status", operator: "=", value: ["In-Progress", "Pending"] })

Global Search searches across all visible values.

Clear buttons:

  • “Clear Filters” removes all column filters but preserves sorts.
  • “Clear All” removes both filters and sorts.

Sorting

  • Click a column header menu to toggle sort: asc → desc → none.
  • Multi-sort is supported. The header shows sort order and direction numbers when multiple columns are active.
enableSorting
enableMultiSort

Pagination

  • Built-in controls: rows per page, current page, first/prev/next/last.
  • Text summary reflects total vs filtered counts.

Props:

  • pageSizeOptions (e.g., [10, 20, 50, 100, 500])
  • initialPageSize

Selection

  • Enable with enableSelection.
  • selectionMode: single or multiple.
  • Header checkbox selects/deselects all rows on the current page in multiple mode.
enableSelection
selectionMode="multiple"
onSelectionChange={(rows) => console.log(rows)}

Row Counts

  • Enable with enableRowCounts.
  • Adds a lightweight numbering column so users can reference row positions easily.
  • When combined with row selection, the numbering column appears immediately after the selection column.
  • Works in every mode (client/server/auto) and respects pagination so numbering stays continuous across pages.
<DataTable
  columns={columns}
  data={myTasks}
  enableRowCounts
  enableSelection
/>

Grid View (Card Layout)

The Grid View feature allows you to display table rows as responsive cards, ideal for mobile devices and compact displays. Users can toggle between traditional table view and grid/card view.

Enabling Grid View

To enable grid view, provide both enableGridView={true} and a renderGridItem function:

<DataTable
  columns={columns}
  data={myTasks}
  tableId="tasks-grid-table"
  enableGridView={true}
  renderGridItem={(row) => (
    <div className="p-4 border rounded-lg bg-card hover:shadow-md transition-shadow">
      <div className="flex items-start justify-between mb-2">
        <span className="font-mono text-sm text-muted-foreground">{row.id}</span>
        <Badge>{row.status}</Badge>
      </div>
      <h3 className="font-semibold text-sm mb-2">{row.title}</h3>
      <div className="flex items-center gap-2">
        <span className="text-sm text-muted-foreground">{row.assignee}</span>
      </div>
    </div>
  )}
  enableSelection
  selectionMode="multiple"
  onSelectionChange={(rows) => console.log("Selected:", rows)}
/>

Grid View Features

  • Toggle Button: A Grid/Table toggle button appears in the toolbar when grid view is enabled
  • Responsive Layout: Cards automatically adjust to screen size using CSS Grid with auto-fit
  • Selection Support: Checkbox appears on each card when enableSelection={true} - perfect for compare features
  • Row Click: Clicking a card triggers onRowClick callback
  • Consistent Heights: Cards in the same row maintain equal heights
  • Mobile Default: On mobile devices (< 768px), grid view is automatically selected by default
  • Persistence: View mode preference is saved to localStorage per table (requires tableId)

Grid View Props

| Property | Type | Required | Default | Description | |----------|------|----------|---------|-------------| | enableGridView | boolean | ❌ Optional | false | Enables the grid/table toggle button | | renderGridItem | (row: T) => React.ReactNode | ⚠️ Conditional | - | Custom card renderer function. Required when enableGridView={true} |

renderGridItem Function

The renderGridItem function receives a single row and returns a React element representing the card:

renderGridItem={(row) => (
  // Your custom card design
  <div className="p-4 border rounded-lg">
    <h3>{row.title}</h3>
    <p>{row.description}</p>
  </div>
)}

Note: Selection styling (ring highlight) is automatically applied by the DataTable when a card is selected. You don't need to handle selection styles in your renderGridItem function.

Grid View Behavior

Toolbar Changes in Grid View:

  • ✅ Global search remains visible
  • ✅ Clear Filters / Clear All buttons remain visible
  • ✅ Grid/Table toggle button visible
  • ❌ Columns dropdown (visibility/reorder) is hidden

Selection in Grid View:

  • Checkbox appears in the top-right corner of each card
  • Supports both single (radio) and multiple (checkbox) selection modes
  • Selected cards show a primary ring highlight

Mobile Behavior:

  • Automatically defaults to grid view on screens < 768px
  • View preference is persisted per table in localStorage
  • Users can still toggle to table view if needed

Complete Grid View Example

import { DataTable, type ColumnDef } from "@algodomain/smart-datatable"
import { Badge } from "@/components/ui/badge"

interface Task {
  id: string
  title: string
  status: string
  priority: string
  assignee: string
  estimatedHours: number
}

const columns: ColumnDef<Task>[] = [
  { id: "id", header: "ID", accessorKey: "id" },
  { id: "title", header: "Title", accessorKey: "title" },
  { id: "status", header: "Status", accessorKey: "status" },
  { id: "priority", header: "Priority", accessorKey: "priority" },
  { id: "assignee", header: "Assignee", accessorKey: "assignee" },
  { id: "estimatedHours", header: "Est. Hours", accessorKey: "estimatedHours" },
]

export function TasksPage() {
  return (
    <DataTable
      columns={columns}
      data={tasks}
      tableId="tasks-grid-table"
      
      // Grid View Configuration
      enableGridView={true}
      renderGridItem={(row) => (
        <div className="p-4 border rounded-lg bg-card hover:shadow-md transition-shadow">
          {/* Header */}
          <div className="flex items-start justify-between mb-2">
            <span className="font-mono text-sm text-muted-foreground">{row.id}</span>
            <Badge
              className={
                row.status === "Completed" ? "bg-green-100 text-green-800" :
                row.status === "In-Progress" ? "bg-blue-100 text-blue-800" :
                "bg-yellow-100 text-yellow-800"
              }
            >
              {row.status}
            </Badge>
          </div>
          
          {/* Title */}
          <h3 className="font-semibold text-sm mb-2 line-clamp-2">{row.title}</h3>
          
          {/* Assignee */}
          <div className="flex items-center gap-2 mb-2">
            <div className="w-6 h-6 rounded-full bg-gray-300 flex items-center justify-center text-xs">
              {row.assignee.split(" ").map(n => n[0]).join("")}
            </div>
            <span className="text-sm text-muted-foreground">{row.assignee}</span>
          </div>
          
          {/* Footer */}
          <div className="flex items-center justify-between text-sm">
            <span className={
              row.priority === "High" ? "text-red-600 font-medium" :
              row.priority === "Medium" ? "text-yellow-600 font-medium" :
              "text-green-600 font-medium"
            }>
              {row.priority}
            </span>
            <span className="text-muted-foreground">{row.estimatedHours}h</span>
          </div>
        </div>
      )}
      
      // Other features work in both views
      enableSelection
      selectionMode="multiple"
      enableFiltering
      enableGlobalSearch
      enableSorting
      pageSizeOptions={[6, 12, 24]}
      initialPageSize={6}
      onRowClick={(row) => console.log("Clicked:", row)}
      onSelectionChange={(rows) => console.log("Selected:", rows)}
    />
  )
}

Column Management

  • Visibility toggle in Columns menu.
  • Reorder with up/down controls in Columns menu. State persists via localStorage.
  • Resize by dragging the column border (enable with enableColumnResize).
  • Freeze/unfreeze individual columns (left-side sticky marker) with enableColumnFreeze.
  • Text wrapping toggle per column from the header menu. Persisted via localStorage.
  • 🔄 Reset All Settings - Comprehensive reset button in Column Settings dropdown that:
    • Resets column order to original sequence
    • Shows all hidden columns
    • Resets column widths to defaults
    • Unfreezes all columns
    • Resets text wrapping to column defaults
    • Clears localStorage for fresh start

Props:

enableColumnVisibility
enableColumnReorder
enableColumnResize
enableColumnFreeze

Column width controls in ColumnDef:

width?: number
minWidth?: number // default 80
maxWidth?: number // default 500
resizable?: boolean // default true; set false to disable per-column
textWrap?: boolean // default false

Column Reset Feature

The reset button (🔄) in the Column Settings dropdown provides a comprehensive "factory reset" for all column configurations:

What gets reset:

  • ✅ Column order → Original sequence from columns prop
  • ✅ Column visibility → All columns visible (unless visible: false in definition)
  • ✅ Column widths → Original widths from column definitions
  • ✅ Frozen columns → No frozen columns (unless frozen: true in definition)
  • ✅ Text wrapping → Original wrapping settings from column definitions
  • ✅ localStorage → Completely cleared for fresh start

How to use:

  1. Click "Columns" button in table toolbar
  2. Click the 🔄 icon in the top-right of the dropdown
  3. All settings reset instantly

Perfect for:

  • User training and onboarding
  • Testing and debugging
  • User support scenarios
  • Starting fresh between analysis sessions

CRUD (Add/Edit/Delete)

The DataTable can show Add Record, Edit, and Delete actions when crudEndpoint is set. Forms are built from column definitions using SmartForms.

Props:

| Prop | Type | Default | Description | |------|------|---------|-------------| | enableAddRecord | boolean | false | Show Add Record button in toolbar | | addRecordButtonText | string | 'Add Record' | Label for Add button and form | | enableEditRecord | boolean | false | Enable edit action on rows | | editRecordButtonText | string | 'Edit Record' | Label for Edit form | | enableDeleteRecord | boolean | false | Enable delete action on rows | | deleteRecordButtonText | string | 'Delete' | Label for Delete action button | | deleteRecordConfirmText | string | 'Are you sure you want to delete this record?' | Confirmation dialog message |

ColumnDef additions:

  • isPrimaryKey?: boolean – When true, that column's accessorKey is the primary key. Exactly one column must have isPrimaryKey: true when Edit or Delete is enabled.
  • editable?: boolean – When false, column is excluded from Add/Edit forms. Default true.
  • smartform?: { showField?, required?, fullWidth? } – Form config: showField (default true), required (default false), fullWidth (default false).

Column-to-field mapping:

  • dataType: 'date' or filterType: 'date'SmartDatePicker
  • dataType: 'boolean' or filterType: 'boolean'SmartCheckbox
  • filterType: 'select' with filterOptionsSmartSelect
  • dataType: 'number'SmartInput type="number"
  • dataType: 'string' (default) → SmartInput type="text"

Escape hatches (custom handlers):

| Prop | Type | Description | |------|------|-------------| | onAddRecord | () => void | If provided, Add button calls this instead of opening built-in form | | onEditRecord | (row: T) => void | If provided, Edit action calls this with the row instead of built-in form | | onDeleteRecord | (row: T) => void | If provided, Delete action calls this with the row instead of built-in confirmation + DELETE |

Use escape hatches when you need custom forms, different form libraries, or client-only updates without an API.

CRUD requirements:

  • Built-in Add/Edit/Delete requires crudEndpoint. If it's missing, an inline error is shown and Add/Edit/Delete are disabled.
  • Edit and Delete require exactly one column with isPrimaryKey: true.
  • For client-only CRUD (no API), use the escape hatches.

Tailwind for SmartForms (when using CRUD):

Add SmartForms to your Tailwind @source so its classes are included:

@source "../node_modules/@algodomain/smart-forms/dist/index.js";

Peer dependencies for CRUD:

When using Add/Edit forms, ensure these are installed: @algodomain/smart-forms, zod, react-toastify, date-fns.

Persistence & localStorage

The DataTable automatically persists user settings to localStorage for a seamless experience across page reloads.

Automatic Persistence

By default, the following settings are automatically saved and restored:

  • Column Visibility - Show/hide column states
  • Column Order - Custom column reordering
  • Column Widths - Resized column dimensions
  • Frozen Columns - Column freeze states
  • Text Wrapping - Column text wrapping preferences
  • Filters - Active column filters and global search
  • Sorting - Sort configuration (single or multi-column)
  • Pagination - Current page and page size

Table Identification

Each table needs a unique identifier to prevent localStorage conflicts:

<DataTable
  columns={columns}
  data={data}
  tableId="my-unique-table-id" // Required for persistence
/>

localStorage Key Format:

algodomain-smart-dt-{tableId}-{columnHash}

Example: algodomain-smart-dt-user-table-abc123def456

Persistence Controls

Fine-tune what gets persisted with granular controls:

<DataTable
  columns={columns}
  data={data}
  tableId="my-table"
  enablePersistence={true}           // Master switch (default: true)
  persistFilters={true}              // Persist filters & search (default: true)
  persistSorting={true}              // Persist sort config (default: true)
  persistColumnSettings={true}       // Persist column settings (default: true)
/>

Disabling Persistence

// Disable all persistence
<DataTable
  columns={columns}
  data={data}
  enablePersistence={false}
/>

// Selective persistence
<DataTable
  columns={columns}
  data={data}
  tableId="my-table"
  persistFilters={false}        // Don't persist filters
  persistSorting={false}        // Don't persist sorting
  persistColumnSettings={true}  // But do persist column settings
/>

Multi-Table Applications

When using multiple DataTables in the same application, each needs a unique tableId:

<DataTable
  columns={userColumns}
  data={users}
  tableId="users-table"  // Unique ID
/>

<DataTable
  columns={orderColumns}
  data={orders}
  tableId="orders-table" // Different unique ID
/>

localStorage Management

The DataTable handles localStorage operations with error handling:

  • Automatic cleanup when localStorage is full
  • Graceful fallback if localStorage access fails
  • Cross-tab synchronization for real-time updates
  • Data validation to prevent corruption

Reset and Clear

Use the 🔄 reset button in Column Settings to:

  • Reset all column settings to defaults
  • Clear localStorage data for fresh start
  • Restore original table configuration

Fixed Headers and Table Height

  • Provide maxHeight (e.g., "400px" or "50vh") to enable sticky headers with a scrollable body.
  • Horizontal scroll sync is handled between header and body for a smoother UX.
<DataTable
  columns={columns}
  data={myTasks}
  maxHeight="400px"
/>

Styling

  • Uses Tailwind design tokens bound to CSS variables in your app's index.css (or Tailwind entry).
  • Key visual options:
    • showColumnBorders: dashed/dotted borders for dense data sets
    • headerBackgroundColor: customize header background (non-Tailwind inline color supported) - DEPRECATED: use theme.header.backgroundColor instead
    • className: apply additional Tailwind classes
    • theme: comprehensive theme configuration for custom styling

Basic Styling

<DataTable
  columns={columns}
  data={myTasks}
  showColumnBorders
  headerBackgroundColor="#f8f9fa" // Legacy prop - still works
  className="p-4"
/>

Custom Theme Styling

The new theme prop provides comprehensive styling control with support for both hex codes and Tailwind classes. This powerful system allows you to customize every aspect of the table's appearance while maintaining full backward compatibility.

Basic Theme Example:

<DataTable
  columns={columns}
  data={myTasks}
  theme={{
    header: {
      backgroundColor: '#add8e6', // Light blue header (hex)
      textColor: 'text-gray-900' // Dark text (Tailwind class)
    },
    rows: {
      backgroundColor: '#FFFFC5', // Light yellow rows (hex)
      hoverBackgroundColor: 'hover:bg-blue-50', // Hover effect (Tailwind)
      selectedBackgroundColor: '#e6f3ff' // Selected rows (hex)
    },
    container: {
      borderRadius: 'rounded-lg', // Tailwind class
      boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1)' // CSS shadow
    }
  }}
/>

Complete Theme Examples

Corporate Theme:

<DataTable
  theme={{
    header: {
      backgroundColor: '#1e40af', // Blue header
      textColor: 'text-white', // White text
      borderColor: 'border-blue-600'
    },
    rows: {
      backgroundColor: '#f8fafc', // Light gray rows
      hoverBackgroundColor: 'hover:bg-blue-50', // Subtle blue hover
      selectedBackgroundColor: '#dbeafe', // Light blue selection
      alternateRowBackgroundColor: '#f1f5f9' // Striped rows
    },
    container: {
      borderStyle: 'border-solid',
      borderColor: 'border-gray-200',
      borderRadius: 'rounded-lg',
      boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.1)'
    },
    columns: {
      borderColor: 'border-gray-100',
      separatorColor: '#e5e7eb'
    }
  }}
/>

Modern Dark Theme:

<DataTable
  theme={{
    header: {
      backgroundColor: '#374151', // Dark gray header
      textColor: 'text-white',
      borderColor: 'border-gray-600'
    },
    rows: {
      backgroundColor: '#1f2937', // Dark rows
      hoverBackgroundColor: 'hover:bg-gray-700',
      selectedBackgroundColor: '#4b5563',
      textColor: 'text-gray-100'
    },
    container: {
      backgroundColor: '#111827',
      borderStyle: 'border-solid',
      borderColor: 'border-gray-600',
      borderRadius: 'rounded-xl',
      boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.3)'
    },
    controls: {
      backgroundColor: '#374151',
      textColor: 'text-gray-200',
      buttonBackgroundColor: '#4b5563',
      buttonHoverBackgroundColor: '#6b7280'
    }
  }}
/>

Border Style Examples

No Border (Clean Look):

<DataTable
  theme={{
    container: {
      borderStyle: 'border-none',
      boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1)' // Add shadow for definition
    }
  }}
/>

Dashed Border:

<DataTable
  theme={{
    container: {
      borderStyle: 'border-dashed',
      borderColor: 'border-blue-300'
    }
  }}
/>

Dotted Border:

<DataTable
  theme={{
    container: {
      borderStyle: 'border-dotted',
      borderColor: 'border-red-400'
    }
  }}
/>

Double Border:

<DataTable
  theme={{
    container: {
      borderStyle: 'border-double',
      borderColor: 'border-purple-500'
    }
  }}
/>

Custom CSS Border:

<DataTable
  theme={{
    container: {
      borderStyle: '3px solid',
      borderColor: '#ff0000'
    }
  }}
/>

Mixed Border Styles:

<DataTable
  theme={{
    container: {
      borderStyle: '2px dashed',
      borderColor: '#10b981', // Green dashed border
      borderRadius: 'rounded-2xl',
      padding: '16px'
    },
    columns: {
      borderStyle: 'border-solid',
      borderColor: 'border-gray-200'
    }
  }}
/>

Advanced Theme Combinations

Minimalist Theme:

<DataTable
  theme={{
    header: {
      backgroundColor: 'bg-transparent',
      textColor: 'text-gray-700',
      borderColor: 'border-transparent'
    },
    rows: {
      backgroundColor: 'bg-transparent',
      hoverBackgroundColor: 'hover:bg-gray-50',
      borderColor: 'border-transparent'
    },
    container: {
      borderStyle: 'border-none',
      backgroundColor: 'bg-transparent'
    }
  }}
/>

High Contrast Theme:

<DataTable
  theme={{
    header: {
      backgroundColor: '#000000',
      textColor: '#ffffff',
      borderColor: '#ffffff'
    },
    rows: {
      backgroundColor: '#ffffff',
      hoverBackgroundColor: '#f0f0f0',
      selectedBackgroundColor: '#e0e0e0',
      textColor: '#000000',
      borderColor: '#000000'
    },
    container: {
      borderStyle: 'border-solid',
      borderColor: '#000000',
      borderRadius: 'rounded-none'
    }
  }}
/>

Theme Configuration Options

interface DataTableTheme {
  header?: {
    backgroundColor?: string; // hex like '#f8f9fa' or Tailwind like 'bg-blue-100'
    textColor?: string; // hex or Tailwind like 'text-gray-900'
    borderColor?: string; // hex or Tailwind like 'border-gray-200'
    borderStyle?: string; // CSS border style like '1px solid'
  };
  rows?: {
    backgroundColor?: string; // default row background
    hoverBackgroundColor?: string; // hover state
    selectedBackgroundColor?: string; // selected row background
    alternateRowBackgroundColor?: string; // for striped tables
    textColor?: string;
    borderColor?: string;
    borderStyle?: string;
  };
  container?: {
    backgroundColor?: string;
    borderColor?: string; // hex like '#ff0000' or Tailwind like 'border-red-500'
    borderStyle?: string; // Tailwind like 'border-dashed', 'border-dotted', 'border-none' or CSS like '1px solid'
    borderRadius?: string; // hex or Tailwind like 'rounded-lg'
    padding?: string; // CSS padding
    boxShadow?: string; // CSS shadow
  };
  columns?: {
    borderColor?: string;
    borderStyle?: string;
    separatorColor?: string; // for column separators
  };
  controls?: {
    backgroundColor?: string;
    textColor?: string;
    buttonBackgroundColor?: string;
    buttonHoverBackgroundColor?: string;
    borderColor?: string;
  };
}

Theme Best Practices

Color Input Types:

  • Hex Colors: Use for exact color control (#ff0000, #3b82f6)
  • Tailwind Classes: Use for design system consistency (bg-blue-500, text-gray-900)
  • CSS Values: Use for custom properties (rgb(255, 0, 0), rgba(0, 0, 0, 0.5))

Border Style Guidelines:

  • Tailwind Classes: border-none, border-dashed, border-dotted, border-double, border-solid
  • CSS Values: 1px solid, 2px dashed, 3px dotted
  • Mixed Usage: Combine Tailwind for style, hex for color (borderStyle: 'border-dashed' + borderColor: '#ff0000')

Performance Tips:

  • Use Tailwind classes when possible for better CSS optimization
  • Prefer hex colors for brand-specific colors
  • Use CSS values for precise control (shadows, padding, margins)

Accessibility Considerations:

  • Ensure sufficient color contrast ratios (WCAG AA: 4.5:1, AAA: 7:1)
  • Test with dark and light themes
  • Consider color-blind users when choosing color combinations

Troubleshooting Theme Issues

Common Issues and Solutions:

  1. Styles Not Applying:

    // ❌ Wrong - theme structure
    theme={{
      headerBgColor: '#ff0000' // Wrong property name
    }}
       
    // ✅ Correct - use proper structure
    theme={{
      header: {
        backgroundColor: '#ff0000'
      }
    }}
  2. Border Styles Not Working:

    // ❌ Wrong - invalid border style
    theme={{
      container: {
        borderStyle: 'dashed' // Missing 'border-' prefix
      }
    }}
       
    // ✅ Correct - use proper Tailwind class
    theme={{
      container: {
        borderStyle: 'border-dashed'
      }
    }}
  3. Colors Not Showing:

    // ❌ Wrong - invalid color format
    theme={{
      header: {
        backgroundColor: 'blue' // Not a valid CSS color
      }
    }}
       
    // ✅ Correct - use hex or Tailwind
    theme={{
      header: {
        backgroundColor: '#3b82f6' // Hex color
        // OR
        backgroundColor: 'bg-blue-500' // Tailwind class
      }
    }}
  4. Selected Row Colors Not Working:

    // ✅ Ensure selection is enabled
    <DataTable
      enableSelection={true}
      selectionMode="multiple"
      theme={{
        rows: {
          selectedBackgroundColor: '#e6f3ff'
        }
      }}
    />

Backward Compatibility

The new theme system is fully backward compatible:

  • Existing headerBackgroundColor prop continues to work
  • All default styles remain unchanged
  • Theme values override legacy props when both are provided

Migration Guide:

// Old way (still works)
<DataTable
  headerBackgroundColor="#f8f9fa"
/>

// New way (recommended)
<DataTable
  theme={{
    header: {
      backgroundColor: '#f8f9fa'
    }
  }}
/>

// Both together (theme takes precedence)
<DataTable
  headerBackgroundColor="#f8f9fa" // Legacy prop
  theme={{
    header: {
      backgroundColor: '#add8e6' // This will be used
    }
  }}
/>

Backend Integration with ORM Adapters

Use backendORM to shape the POST request payload into the format expected by your backend:

  • custom (default): pass-through { page, pageSize, sort, filters, logicalOperator }.
  • prisma: builds { where, orderBy, skip, take } with proper Prisma query format and global search support.
  • mongoose: builds { filter, sort, skip, limit } with $and/$or chains and regex for text.
  • typeorm: builds { where, order, skip, take } using a neutral, mappable format.

Example (server mode):

<DataTable
  columns={columns}
  crudEndpoint="/api/tasks"
  searchAndFilterEndpoint="/api/tasks"
  mode="server"
  backendORM="mongoose"
  initialPageSize={25}
/>

Server responsibilities:

  • Accept POST requests with the transformed payload.
  • Apply filtering, sorting, and pagination.
  • Return a JSON response matching { data, totalRecords, page, pageSize, totalPages }.

Prisma Backend Setup

For Prisma ORM, your backend should handle the transformed payload:

// Example Prisma backend endpoint
export const searchOrders = async (req, res, next) => {
  try {
    // DataTable sends the complete Prisma query object
    const prismaQuery = req.body; // { where, orderBy, skip, take }

    // Execute the Prisma query
    const result = await prisma.customerOrder.findMany(prismaQuery);

    // Get total count for pagination
    const { where } = prismaQuery;
    const total = await prisma.customerOrder.count({ where });

    // Calculate pagination info
    const skip = prismaQuery.skip || 0;
    const take = prismaQuery.take || 10;
    const page = Math.floor(skip / take) + 1;
    const totalPages = Math.ceil(total / take);

    res.json({
      data: result,
      totalRecords: total,
      page: page,
      pageSize: take,
      totalPages: totalPages
    });
  } catch (error) {
    next(error);
  }
};

Route Setup:

// Detect DataTable requests by Prisma format
router.post('/', (req, res, next) => {
  if (req.body.skip !== undefined || req.body.take !== undefined || 
      req.body.where !== undefined || req.body.orderBy !== undefined) {
    return searchOrders(req, res, next);
  }
  // Handle other POST requests...
});

Error Handling

  • If the server responds with a non-OK status, an error message is shown in-table.
  • Provide onError to handle errors globally (e.g., toast notifications).
onError={(err) => {
  console.error("DataTable error", err)
}}

Performance Tips

  • Prefer server or auto mode for very large datasets.
  • Use filterType: "select" for categorical columns to reduce user input and improve clarity.
  • Provide dataType for accurate operators and comparisons.
  • Consider virtualizing rows for extremely large client datasets (outside this component).

Full Minimal Examples

Client with data prop (minimal):

<DataTable columns={columns} data={myTasks} />

Client with API endpoint (minimal):

<DataTable columns={columns} crudEndpoint="/api/tasks" mode="client" />

Server (minimal):

<DataTable columns={columns} crudEndpoint="/api/tasks" searchAndFilterEndpoint="/api/tasks/search" mode="server" />

Auto (minimal):

<DataTable columns={columns} crudEndpoint="/api/tasks" searchAndFilterEndpoint="/api/tasks/search" mode="auto" />

Reference

Complete Properties Reference

| Property | Type | Required | Default | Description | Dependencies & Notes | |----------|------|----------|---------|-------------|---------------------| | Data Configuration | | columns | ColumnDef<T>[] | ✅ Required | - | Column definitions for the table | Must be provided | | data | T[] | ⚠️ Conditional | - | Data array for client mode | Required if mode is "client" or undefined (but not with crudEndpoint) | | crudEndpoint | string | ⚠️ Conditional | - | Base URL for CRUD (GET load, POST create, PUT/:id update, DELETE/:id) | Required for API-backed data; for CRUD when enableAddRecord/enableEditRecord/enableDeleteRecord | | searchAndFilterEndpoint | string | ⚠️ Conditional | - | POST endpoint for paginated search | Required when mode is "server" or "auto" | | auth | AuthConfig | ❌ Optional | - | Authentication for API requests | accessTokenKey, refreshTokenKey, refreshUrl | | backendORM | "custom" \| "prisma" \| "mongoose" \| "typeorm" | ❌ Optional | "custom" | Backend ORM adapter for request shaping | Only used in server/auto modes | | Table Identification | | tableId | string | ❌ Optional | Auto-generated | Unique identifier for localStorage | Auto-generated from column IDs if not provided | | Pagination | | mode | "client" \| "server" \| "auto" | ❌ Optional | undefined (client) | Data handling mode | Determines which other props are required | | serverPageSize | number | ❌ Optional | 100 | Page size for server requests | Only used in server/auto modes | | pageSizeOptions | number[] | ❌ Optional | [10, 20, 50, 100, 500] | Available page size options | Used for page size selector | | initialPageSize | number | ❌ Optional | First from pageSizeOptions | Initial page size | Must be one of the values in pageSizeOptions | | Selection | | enableSelection | boolean | ❌ Optional | false | Enable row selection | - | | selectionMode | "single" \| "multiple" | ❌ Optional | "single" | Selection behavior | Requires enableSelection={true} | | enableRowCounts | boolean | ❌ Optional | false | Displays a numbering column | Appears before selection column and follows pagination | | onSelectionChange | (rows: T[]) => void | ❌ Optional | - | Selection change callback | Recommended when using selection | | Filtering | | enableFiltering | boolean | ❌ Optional | true | Enable column filtering | - | | enableGlobalSearch | boolean | ❌ Optional | true | Enable global search | - | | savedFilters | SavedFilter[] | ❌ Optional | [] | Pre-defined saved filters | - | | onSaveFilter | (filter: SavedFilter) => void | ❌ Optional | - | Save filter callback | Recommended when using saved filters | | Sorting | | enableSorting | boolean | ❌ Optional | true | Enable column sorting | - | | enableMultiSort | boolean | ❌ Optional | true | Enable multi-column sorting | Requires enableSorting={true} | | Column Management | | enableColumnVisibility | boolean | ❌ Optional | true | Show/hide columns | - | | enableColumnReorder | boolean | ❌ Optional | false | Drag to reorder columns | - | | enableColumnResize | boolean | ❌ Optional | false | Resize columns by dragging | - | | enableColumnFreeze | boolean | ❌ Optional | false | Freeze columns left/right | - | | enableHeaderFreeze | boolean | ❌ Optional | true | Enable sticky headers | - | | Caching | | cacheTimeout | number | ❌ Optional | 300000 (5 min) | Cache timeout in milliseconds | Only used in auto mode | | maxCacheSize | number | ❌ Optional | 1000 | Maximum cached records | Only used in auto mode | | enablePrefetch | boolean | ❌ Optional | false | Prefetch next pages | Only used in auto mode | | Persistence | | enablePersistence | boolean | ❌ Optional | true | Enable localStorage persistence | Master switch for all persistence | | persistFilters | boolean | ❌ Optional | true | Persist filters and search | Requires enablePersistence={true} | | persistSorting | boolean | ❌ Optional | true | Persist sort configuration | Requires enablePersistence={true} | | persistColumnSettings | boolean | ❌ Optional | true | Persist column settings | Requires enablePersistence={true} | | Styling | | className | string | ❌ Optional | - | Additional CSS classes | Applied to table container | | density | "compact" \| "normal" \| "comfortable" | ❌ Optional | "normal" | Row density | Affects row height and spacing | | showColumnBorders | boolean | ❌ Optional | false | Show column borders | - | | maxHeight | string \| number | ❌ Optional | - | Maximum table height | Enables sticky headers automatically | | headerBackgroundColor | string | ❌ Optional | "#f8f9fa" | Header background color | CSS color value | | Grid View | | enableGridView | boolean | ❌ Optional | false | Enable grid/table toggle | Requires renderGridItem to be provided | | renderGridItem | (row: T) => React.ReactNode | ⚠️ Conditional | - | Custom card renderer function | Required when enableGridView={true} | | CRUD | | enableAddRecord | boolean | ❌ Optional | false | Show Add Record button in toolbar | Requires crudEndpoint | | addRecordButtonText | string | ❌ Optional | 'Add Record' | Label for Add button and form | - | | enableEditRecord | boolean | ❌ Optional | false | Enable edit action on rows | Requires crudEndpoint and one column with isPrimaryKey: true | | editRecordButtonText | string | ❌ Optional | 'Edit Record' | Label for Edit form | - | | enableDeleteRecord | boolean | ❌ Optional | false | Enable delete action on rows | Requires crudEndpoint and one column with isPrimaryKey: true | | deleteRecordButtonText | string | ❌ Optional | 'Delete' | Label for Delete action button | - | | deleteRecordConfirmText | string | ❌ Optional | 'Are you sure...' | Confirmation dialog message | - | | onAddRecord | () => void | ❌ Optional | - | Custom Add handler (escape hatch) | Replaces built-in form when provided | | onEditRecord | (row: T) => void | ❌ Optional | - | Custom Edit handler (escape hatch) | Replaces built-in form when provided | | onDeleteRecord | (row: T) => void | ❌ Optional | - | Custom Delete handler (escape hatch) | Replaces built-in confirmation when provided | | Events | | onRowClick | (row: T) => void | ❌ Optional | - | Row click handler | - | | onError | (error: Error) => void | ❌ Optional | - | Error handler | Recommended for production |

Property Dependencies & Requirements

Mode-Specific Requirements

Client Mode (mode="client" or undefined):

  • columns (required)
  • data OR crudEndpoint (one required, but not both)
  • ❌ Cannot use both data and crudEndpoint simultaneously

Server Mode (mode="server"):

  • columns (required)
  • crudEndpoint and searchAndFilterEndpoint (required)
  • data (not used)

Auto Mode (mode="auto"):

  • columns (required)
  • crudEndpoint and searchAndFilterEndpoint (required)
  • data (not used)

Conditional Requirements

Selection Features:

  • selectionMode requires enableSelection={true}
  • onSelectionChange recommended when using selection

Multi-Column Sorting:

  • enableMultiSort requires enableSorting={true}

Persistence Features:

  • persistFilters, persistSorting, persistColumnSettings require enablePersistence={true}

Caching Features (Auto mode only):

  • cacheTimeout, maxCacheSize, enablePrefetch only apply in auto mode

Grid View Features:

  • renderGridItem is required when enableGridView={true}
  • Grid view defaults to active on mobile (< 768px) when enabled
  • View mode preference is persisted per table in localStorage (requires tableId)

Property Conflicts

Mutually Exclusive:

  • data and crudEndpoint cannot both be used in client mode (will show validation error)
  • enablePersistence={false} disables all persistence features regardless of individual persist* settings

Recommended Combinations:

  • Use tableId when enablePersistence={true} for predictable localStorage keys
  • Use onError handler in production applications
  • Use onSelectionChange when enableSelection={true}
  • Use onSaveFilter when providing savedFilters

ColumnDef Properties Reference

| Property | Type | Required | Default | Description | Dependencies & Notes | |----------|------|----------|---------|-------------|---------------------| | Basic Configuration | | id | string | ✅ Required | - | Unique column identifier | Must be unique across all columns | | header | string | ✅ Required | - | Column header text | Displayed in table header | | accessorKey | keyof T | ✅ Required | - | Key to access data from row object | Must match property in data objects | | Customization | | cell | (row: T) => React.ReactNode | ❌ Optional | - | Custom cell renderer | Receives full row object for rendering | | Functionality | | sortable | boolean | ❌ Optional | true | Enable sorting for this column | Requires enableSorting={true} on DataTable | | filterable | boolean | ❌ Optional | true | Enable filtering for this column | Requires enableFiltering={true} on DataTable | | isPrimaryKey | boolean | ❌ Optional | false | Mark as primary key | Exactly one column when Edit/Delete enabled | | editable | boolean | ❌ Optional | true | Include in Add/Edit forms | When false, excluded from dynamic form | | smartform | { showField?, required?, fullWidth? } | ❌ Optional | - | Form config | showField (default true), required (default false), fullWidth (default false) | | dataType | "string" \| "number" \| "date" \| "boolean" | ❌ Optional | Auto-detected | Data type for filtering/sorting | Used for proper filter operators | | filterType | "text" \| "number" \| "date" \| "select" \| "boolean" | ❌ Optional | Based on dataType | Filter input type | Determines filter UI component | | filterOptions | { label: string; value: unknown }[] | ⚠️ Conditional | - | Options for select filter | Required when filterType="select" | | Sizing | | width | number | ❌ Optional | 150 | Column width in pixels | Used for initial column width | | minWidth | number | ❌ Optional | 80 | Minimum column width | Prevents columns from becoming too narrow | | maxWidth | number | ❌ Optional | 500 | Maximum column width | Prevents columns from becoming too wide | | resizable | boolean | ❌ Optional | true | Allow column resizing | Requires enableColumnResize={true} on DataTable | | Visibility & Layout | | visible | boolean | ❌ Optional | true | Initial column visibility | Can be changed by user if enableColumnVisibility={true} | | frozen | boolean | ❌ Optional | false | Freeze column to left/right | Requires enableColumnFreeze={true} on DataTable | | frozenPosition | "left" \| "right" | ❌ Optional | "left" | Position for frozen columns | Only applies when frozen={true} | | textWrap | boolean | ❌ Optional | false | Enable text wrapping in cells | Controls text overflow behavior |

ColumnDef Dependencies & Requirements

Conditional Requirements

Filter Configuration:

  • filterOptions is required when filterType="select"
  • filterType should match dataType for optimal UX

Sizing Constraints:

  • minWidth should be ≤ widthmaxWidth
  • width should be within reasonable bounds (50-1000px)

Functionality Dependencies:

  • Column sorting requires both sortable={true} and enableSorting={true} on DataTable
  • Column filtering requires both filterable={true} and enableFiltering={true} on DataTable
  • Column resizing requires both resizable={true} and enableColumnResize={true} on DataTable
  • Column freezing requires both frozen={true} and enableColumnFreeze={true} on DataTable

Best Practices

Column IDs:

  • Use descriptive, kebab-case IDs: "user-name", "created-at"
  • Avoid spaces and special characters
  • Keep IDs consistent across different table instances

Data Types:

  • Set dataType explicitly for proper filtering/sorting
  • Use "date" for date columns to enable date picker filters
  • Use "boolean" for true/false columns

Filter Types:

  • Use "select" for columns with limited, known values
  • Use "date" for date columns
  • Use "number" for numeric columns
  • Use "text" for general string columns

Sizing:

  • Set reasonable width values based on content
  • Use minWidth to prevent columns from becoming unusable
  • Use maxWidth to prevent columns from taking too much space

Operators:

type FilterOperator =
  | "=" | "!=" | "contains" | "startsWith" | "endsWith" | "isEmpty" | "isNotEmpty"
  | ">" | "<" | ">=" | "<=" | "between"

Quick Reference & Common Patterns

Minimal Client Table (with data prop)

<DataTable
  columns={columns}
  data={myData}
  tableId="simple-table"
/>

Minimal Client Table (with API endpoint)

<DataTable
  columns={columns}
  crudEndpoint="/api/data"
  mode="client"
  tableId="api-table"
/>

Full-Featured Client Table

<DataTable
  columns={columns}
  data={myData}
  tableId="full-table"
  enableRowCounts
  enableSelection
  selectionMode="multiple"
  enableFiltering
  enableGlobalSearch
  enableSorting
  enableMultiSort
  enableColumnVisibility
  enableColumnReorder
  enableColumnResize
  enableColumnFreeze
  maxHeight="400px"
  showColumnBorders
  onRowClick={(row) => console.log(row)}
  onSelectionChange={(rows) => console.log(rows)}
/>

Server Table with Persistence

<DataTable
  columns={columns}
  crudEndpoint="/api/data"
  searchAndFilterEndpoint="/api/data"
  mode="server"
  tableId="server-table"
  backendORM="prisma"
  enablePersistence={true}
  persistFilters={true}
  persistSorting={true}
  persistColumnSettings={true}
  pageSizeOptions={[10, 25, 50, 100]}
  initialPageSize={25}
/>

Auto Mode with Caching

<DataTable
  columns={columns}
  crudEndpoint="/api/data"
  searchAndFilterEndpoint="/api/data"
  mode="auto"
  tableId="auto-table"
  serverPageSize={50}
  enablePrefetch={true}
  cacheTimeout={600000}
  maxCacheSize={2000}
/>

Disable Persistence

<DataTable
  columns={columns}
  data={myData}
  enablePersistence={false}
  // All persistence features disabled
/>

Selective Persistence

<DataTable
  columns={columns}
  data={myData}
  tableId="selective-table"
  persistFilters={false}        // Don't persist filters
  persistSorting={false}        // Don't persist sorting
  persistColumnSettings={true}  // But do persist column settings
/>

Grid View (Card Layout)

<DataTable
  columns={columns}
  data={myData}
  tableId="grid-table"
  enableGridView={true}
  renderGridItem={(row) => (
    <div className="p-4 border rounded-lg">
      <h3 className="font-semibold">{row.title}</h3>
      <p className="text-muted-foreground">{row.description}</p>
    </div>
  )}
  enableSelection
  selectionMode="multiple"
/>

Column Definition Examples

const columns: ColumnDef<Task>[] = [
  // Basic column
  {
    id: "id",
    header: "ID",
    accessorKey: "id",
    width: 80,
  },
  
  // Sortable and filterable column
  {
    id: "title",
    header: "Title",
    accessorKey: "title",
    sortable: true,
    filterable: true,
    dataType: "string",
    filterType: "text",
    width: 200,
  },
  
  // Select filter column
  {
    id: "status",
    header: "Status",
    accessorKey: "status",
    dataType: "string",
    filterType: "select",
    filterOptions: [
      { label: "Active", value: "active" },
      { label: "Inactive", value: "inactive" },
    ],
  },
  
  // Date column with date picker
  {
    id: "createdAt",
    header: "Created At",
    accessorKey: "createdAt",
    dataType: "date",
    filterType: "date",
    width: 150,
  },
  
  // Custom cell renderer
  {
    id: "actions",
    header: "Actions",
    accessorKey: "id",
    cell: (row) => (
      <button onClick={() => handleEdit(row.id)}>
        Edit
      </button>
    ),
    sortable: false,
    filterable: false,
    width: 100,
  },
];

That's it! You're ready to build powerful, user-friendly tables with client, server, or hybrid workflows.

Dynamic Runtime Controls

The DataTable component supports dynamic runtime control of its features. You can create interactive controls that allow users to enable/disable features on-the-fly without any code changes to the DataTable component itself.

Example: Dynamic Feature Controls

import { useState } from 'react'
import { DataTable, type ColumnDef } from "@algodomain/smart-datatable"
import { Checkbox } from "@/components/ui/checkbox"

interface Task {
  id: string
  title: string
  status: string
}

const columns: ColumnDef<Task>[] = [
  { id: "id", header: "ID", accessorKey: "id" },
  { id: "title", header: "Title", accessorKey: "title" },
  { id: "status", header: "Status", accessorKey: "status" },
]

const data: Task[] = [
  { id: "1", title: "Task 1", status: "Active" },
  { id: "2", title: "Task 2", status: "Completed" },
]

export function DynamicControlsExample() {
  // State for dynamic controls
  const [controls, setControls] = useState({
    enableSelection: true,
    enableFiltering: true,
    enableGlobalSearch: true,
    enableSorting: true,
    enableMultiSort: true,
    enableColumnVisibility: true,
    enableColumnReorder: true,
    enableColumnResize: true,
    enableColumnFreeze: true,
    showColumnBorders: true,
  })

  const handleControlChange = (control: string, value: boolean) => {
    setControls(prev => ({ ...prev, [control]: value }))
  }

  return (
    <div className="space-y-4">
      {/* Dynamic Controls */}
      <div className="bg-gray-50 p-4 rounded-lg">
        <h3 className="font-semibold mb-3">Feature Controls</h3>
        <div className="grid grid-cols-2 md:grid-cols-5 gap-4">
          <div className="flex items-center space-x-2">
            <Checkbox
              id="enableSelection"
              checked={controls.enableSelection}
              onCheckedChange={(checked) => handleControlChange('enableSelection', !!checked)}
            />
            <label htmlFor="enableSelection" className="text-sm">Selection</label>
          </div>
          <div className="flex items-center space-x-2">
            <Checkbox
              id="enableFiltering"
              checked={controls.enableFiltering}
              onCheckedChange={(checked) => handleControlChange('enableFiltering', !!checked)}
            />
            <label htmlFor="enableFiltering" className="text-sm">Filtering</label>
          </div>
          <div className="flex items-center space-x-2">
            <Checkbox
              id="enableGlobalSearch"
              checked={controls.enableGlobalSearch}
              onCheckedChange={(checked) => handleControlChange('enableGlobalSearch', !!checked)}
            />
            <label htmlFor="enableGlobalSearch" className="text-sm">Global Search</label>
          </div>
          <div className="flex items-center space-x-2">
            <Checkbox
              id="enableSorting"
              checked={controls.enableSorting}
              onCheckedChange={(checked) => handleControlChange('enableSorting', !!checked)}
            />
            <label htmlFor="enableSorting" className="text-sm">Sorting</label>
          </div>
          <div className="flex items-center space-x-2">
            <Checkbox
              id="enableColumnVisibility"
              checked={controls.enableColumnVisibility}
              onCheckedChange={(checked) => handleControlChange('enableColumnVisibility', !!checked)}
            />
            <label htmlFor="enableColumnVisibility" className="text-sm">Column Visibility</label>
          </div>
          {/* Add more controls as needed */}
        </div>
      </div>

      {/* DataTable with Dynamic Props */}
      <DataTable
        columns={columns}
        data={data}
        tableId="dynamic-controls-table"
        
        // Dynamic controls from state
        enableSelection={controls.enableSelection}
        enableFiltering={controls.enableFiltering}
        enableGlobalSearch={controls.enableGlobalSearch}
        enableSorting={controls.enableSorting}
        enableMultiSort={controls.enableMultiSort}
        enableColumnVisibility={controls.enableColumnVisibility}
        enableColumnReorder={controls.enableColumnReorder}
        enableColumnResize={controls.enableColumnResize}
        enableColumnFreeze={controls.enableColumnFreeze}
        showColumnBorders={controls.showColumnBorders}
        
        // Fixed properties
        selectionMode="multiple"
        pageSizeOptions={[5, 10, 20]}
        initialPageSize={10}
      />
    </div>
  )
}

Benefits of Dynamic Controls

  • User Experience: Allow users to customize their table experience
  • Feature Discovery: Help users understand available features
  • Flexible Interfaces: Create adaptive UIs that respond to user preferences
  • No Code Changes: The DataTable component remains unchanged
  • Real-time Updates: Features can be toggled instantly without page refresh

Supported Dynamic Properties

All these properties can be controlled dynamically:

  • enableSelection - Row selection functionality
  • enableFiltering - Column filtering
  • enableGlobalSearch - Global search across all columns
  • enableSorting - Column sorting
  • enableMultiSort - Multi-column sorting
  • enableColumnVisibility - Show/hide columns
  • enableColumnReorder - Drag to reorder columns
  • enableColumnResize - Resize columns by dragging
  • enableColumnFreeze - Freeze columns to left/right
  • showColumnBorders - Display column borders

Quick Start Page (minimal)

Create a simple page component (e.g., src/pages/ExamplePage.tsx) and paste the following:

import { DataTable, type ColumnDef } from "@algodomain/smart-datatable"
import "@algodomain/smart-datatable/style.css"

type Task = {
  id: string
  title: string
}

const columns: ColumnDef<Task>[] = [
  { id: "id", header: "ID", accessorKey: "id", sortable: true, filterable: true, dataType: "string" },
  { id: "title", header: "Title", accessorKey: "title", sortable: true, filterable: true, dataType: "string", textWrap: true },
]

const data: Task[] = [
  { id: "1", title: "First task" },
  { id: "2", title: "Second task" },
]

export default function ExamplePage() {
  return (
    <div className="p-4">
      <DataTable
        columns={columns}
        data={data}
        enableFiltering
        enableSorting
        pageSizeOptions={[5, 10, 20]}
        initialPageSize={5}
        showColumnBorders
      />
    </div>
  )
}

Make sure your Tailwind entry (e.g., src/index.css) sources the package so class names are included during build:

/* Ensure Tailwind scans DataTable package classes */
@source "../node_modules/@algodomain/smart-datatable/dist/index.js";
/* OR */
@source "../node_modules/@algodomain/smart-datatable/dist/index.cjs";

Upcoming

  • Authentication support for mode="server" and mode="auto":
    • Access token and refresh token handling for secure API requests
    • Built-in hooks to inject auth headers in server requests
    • Graceful token refresh and retry behavior
    • Backward-compatible configuration via new props