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

@insticc/react-datagrid-2

v1.1.31

Published

A powerful and flexible React data grid component built on top of Material React Table, providing advanced features for data visualization, filtering, sorting, pagination, and row selection.

Readme

DataGrid Component

A powerful and flexible React data grid component built on top of Material React Table, providing advanced features for data visualization, filtering, sorting, pagination, and row selection.

Table of Contents


Installation

npm install @insticc/react-datagrid-2

Required Dependencies

npm install react prop-types material-react-table semantic-ui-react @mui/material @mui/x-date-pickers date-fns bootstrap semantic-ui-css

Basic Usage

import React, { useState } from 'react';
import { DataGrid } from '@insticc/react-datagrid-2';

function MyComponent() {
  const [selectedRows, setSelectedRows] = useState([]);

  const columns = [
    { accessorKey: 'id', header: 'ID', type: 'text' },
    { accessorKey: 'name', header: 'Name', type: 'text' },
    { accessorKey: 'email', header: 'Email', type: 'email' },
    { accessorKey: 'createdAt', header: 'Created', type: 'date', isDateColumn: true },
  ];

  const data = [
    { id: '1', name: 'John Doe', email: '[email protected]', createdAt: '2025-01-15' },
    { id: '2', name: 'Jane Smith', email: '[email protected]', createdAt: '2025-02-20' },
  ];

  return (
    <DataGrid
      columns={columns}
      createRows={data}
      rowKey="id"
      selectData={setSelectedRows}
      hasExcelExport={true}
      hasPdfExport={true}
    />
  );
}

Props Reference

Core Props

columns (required)

  • Type: Array<ColumnDefinition>
  • Description: Array of column definitions that determine how data is displayed
  • Example:
const columns = [
  {
    accessorKey: 'name',        // Key to access data (use with accessorFn or alone)
    header: 'Full Name',        // Column header text
    type: 'text',               // Cell type - formatter applied automatically
    
    // Data Access
    accessorFn: (row) => row.firstName + ' ' + row.lastName,
    
    // Filtering & Sorting
    enableSorting: true,        // Enable/disable sorting (default: true)
    enableColumnFilter: true,   // Enable/disable filtering (default: true)
    enableColumnFilterModes: true, // Allow changing filter modes (default: true)
    filterFn: 'contains',       // Filter function type (default: 'contains')
    sortingFn: 'basic',         // Sorting function (default: 'basic' or custom for dates)
    isDateColumn: false,        // Special handling for dates (default: false)
    
    // Display
    cellClass: 'custom-class',  // CSS class wrapping cell content
    Cell: CustomComponent,      // Optional: Custom cell renderer (receives typeValue)
    enableResizing: true,       // Allow column resizing (default: true)
    grow: true,                 // Column can grow (default: true)
    enableClickToCopy: false,   // Enable click-to-copy (default: false)
    enableColumnActions: false, // Show column actions menu (default: false)
  }
];

Column Rendering Modes:

  • type only: Formatter applied automatically
  • type + cellClass: Formatted content wrapped in div with CSS class
  • type + Cell: Custom component receives typeValue (formatted content) as prop
  • Cell only: Full custom rendering without type formatter

createRows (required)

  • Type: Array<Object>
  • Description: Array of data objects to display in the grid
  • Example:
const data = [
  { id: 1, name: 'John', email: '[email protected]' },
  { id: 2, name: 'Jane', email: '[email protected]' }
];

rowKey (required)

  • Type: string
  • Description: Property name used as unique identifier for each row
  • Example: rowKey="id" or rowKey="uuid"

selectData (required)

  • Type: function
  • Description: Callback function that receives selected rows whenever selection changes
  • Signature: (selectedRows: Array<Object>) => void
  • Example:
const handleSelection = (rows) => {
  console.log('Selected rows:', rows);
  console.log('Selected IDs:', rows.map(r => r.id));
};

<DataGrid selectData={handleSelection} />

Column Configuration

Understanding accessorKey vs accessorFn

The DataGrid provides two ways to access data from your rows:

accessorKey (Simple Access)
  • Type: string
  • Use when: You want to access a property directly from the row object
  • Example:
{
  accessorKey: 'name',  // Accesses row.name
  header: 'Name'
}
accessorFn (Custom Access)
  • Type: (row: Object) => any
  • Use when: You need to compute or transform the value from row data
  • Receives: The complete row object as parameter
  • Returns: The value to be used for display, filtering, and sorting
  • Priority: Overrides accessorKey if both are defined
  • Examples:
// Example 1: Handle null/undefined values with fallback
{
  accessorKey: 'acronym',
  accessorFn: (row) => row.acronym ?? '',
  header: 'Acronym',
  type: 'text'
}

// Example 2: Access nested object properties
{
  accessorKey: 'paperCount',
  accessorFn: (row) => row.numberPapers?.value ?? 0,
  header: 'Papers',
  type: 'integer'
}

// Example 3: Combine multiple fields
{
  accessorKey: 'fullName',
  accessorFn: (row) => `${row.firstName} ${row.lastName}`,
  header: 'Full Name',
  type: 'text'
}

// Example 4: Conditional logic
{
  accessorKey: 'status',
  accessorFn: (row) => row.isActive ? 'Active' : 'Inactive',
  header: 'Status',
  type: 'text'
}

// Example 5: Complex calculations
{
  accessorKey: 'total',
  accessorFn: (row) => (row.price || 0) * (row.quantity || 0),
  header: 'Total',
  type: 'currency'
}

// Example 6: Array to string conversion
{
  accessorKey: 'tags',
  accessorFn: (row) => row.tags?.join(', ') ?? 'No tags',
  header: 'Tags',
  type: 'text'
}

// Example 7: Date formatting for sorting
{
  accessorKey: 'createdDate',
  accessorFn: (row) => row.createdAt ? new Date(row.createdAt).getTime() : 0,
  header: 'Created',
  type: 'date',
  isDateColumn: true
}

Important Notes:

  • accessorFn is executed for every cell render, filter, and sort operation
  • Keep the function lightweight to avoid performance issues
  • The returned value is what gets filtered and sorted (not the original cell display)
  • When using accessorFn, you typically still need accessorKey as a unique identifier for the column
  • accessorFn value is passed to the type formatter (if type is specified)

Performance Tip:

// Bad - Creates new object every time
accessorFn: (row) => ({ value: row.data, label: row.name })

// Good - Returns simple value
accessorFn: (row) => row.data?.value ?? 0

columnVisibilityState

  • Type: object
  • Default: {}
  • Description: Controls which columns are visible/hidden
  • Example:
columnVisibilityState={{
  email: false,    // Hide email column
  phone: true,     // Show phone column
  id: false        // Hide ID column
}}

columnOrder

  • Type: Array<string>
  • Default: []
  • Description: Defines the order of columns by their accessorKey
  • Example:
columnOrder={['name', 'email', 'createdAt', 'id']}

Actions & Toolbar

actions

  • Type: Array<ActionObject>
  • Default: null
  • Description: Array of custom action buttons displayed in the toolbar
  • Action Object Properties:
    • name (string): Button label
    • function (function, required): Callback when button is clicked - receives selected rows or table instance
    • tooltip (string): Tooltip text on hover
    • color (string): Button color - one of: 'blue', 'red', 'green', 'yellow', 'orange', 'black', 'grey', 'teal', 'brown', 'violet', 'purple', 'olive', 'pink'
    • icon (string|element): Icon name (Semantic UI) or React element
    • selectionMode (string): When button is enabled:
      • 'single' - Enabled only when exactly one row is selected
      • 'multi' - Enabled when one or more rows are selected
      • 'always' - Always enabled regardless of selection
    • confirmMessage (string|element): Confirmation message before action executes
    • hasConfirmMessage (boolean): Whether to show confirmation dialog
    • disabled (boolean): Manually disable the button
    • visible (boolean): Show/hide the button (default: true)
    • toggle (boolean): Render as toggle button
    • active (boolean): Toggle button active state (when toggle: true)
    • key (string): Unique React key (defaults to name)
  • Example:
actions={[
  {
    name: 'Delete',
    function: (selectedRows) => handleDelete(selectedRows),
    tooltip: 'Delete selected rows',
    color: 'red',
    icon: 'trash',
    selectionMode: 'multi',
    hasConfirmMessage: true,
    confirmMessage: 'Are you sure you want to delete these rows?'
  },
  {
    name: 'Edit',
    function: (selectedRows) => handleEdit(selectedRows[0]),
    tooltip: 'Edit selected row',
    color: 'blue',
    icon: 'edit',
    selectionMode: 'single'
  },
  {
    name: 'Add New',
    function: () => handleAdd(),
    tooltip: 'Add new record',
    color: 'green',
    icon: 'plus',
    selectionMode: 'always'
  },
  {
    name: 'Archive Mode',
    function: () => toggleArchiveMode(),
    tooltip: 'Toggle archive view',
    color: 'grey',
    icon: 'archive',
    toggle: true,
    active: isArchiveMode,
    selectionMode: 'always'
  }
]}

filterActions

  • Type: Array<FilterActionObject>
  • Default: null
  • Description: Array of toggle filter action buttons displayed in the toolbar for showing/hiding data based on filters
  • Filter Action Object Properties:
    • name (string): Button label
    • function (function, required): Callback when button is toggled - receives current state
    • state (boolean): Current filter state (true = filter active/hidden, false = filter inactive/shown)
    • key (string): Unique React key (defaults to name)
    • tooltip (string): Tooltip text on hover
    • disabled (boolean): Manually disable the button
    • visible (boolean): Show/hide the button (default: true)
  • Button Appearance:
    • When state=true: Orange color with eye-slash icon (items hidden)
    • When state=false: Green color with eye icon (items shown)
  • Example:
const [hideCompleted, setHideCompleted] = useState(false);
const [hideArchived, setHideArchived] = useState(true);

filterActions={[
  {
    name: 'Completed',
    state: hideCompleted,
    function: () => setHideCompleted(!hideCompleted),
    tooltip: 'Toggle completed items visibility',
    key: 'filter-completed'
  },
  {
    name: 'Archived',
    state: hideArchived,
    function: () => setHideArchived(!hideArchived),
    tooltip: 'Toggle archived items visibility',
    disabled: false,
    visible: true
  },
  {
    // Dynamic button text based on state
    state: hideEvents,
    name: hideEvents ? "Hiding Events" : "Showing Events",
    tooltip: hideEvents ? "Show Events" : "Hide Events",
    function: () => setHideEvents(!hideEvents),
    visible: !!handleHideEvents,  // Only show if handler exists
  }
]}

extraActions

  • Type: Array<ExtraActionObject>
  • Default: null
  • Description: Additional custom action buttons displayed on the right side of the toolbar
  • Extra Action Object Properties:
    • function (function, required): Callback when button is clicked
    • content (string|element, required): Button content/label
    • tooltip (string): Tooltip text on hover
    • icon (string|element): Icon name or React element
    • style (object): Custom CSS styles for the button
    • propsButton (object): Additional props passed to Semantic UI Button component
  • Example:
extraActions={[
  {
    function: () => openSettings(),
    content: 'Settings',
    tooltip: 'Open settings',
    icon: 'setting',
    style: { backgroundColor: '#f0f0f0' }
  },
  {
    function: () => downloadReport(),
    content: 'Download',
    tooltip: 'Download report',
    icon: 'download',
    propsButton: { loading: isDownloading }
  }
]}

enableTopToolbar

  • Type: boolean
  • Default: true
  • Description: Show/hide the top toolbar containing actions and pagination

enableBottomToolbar

  • Type: boolean
  • Default: false
  • Description: Show/hide the bottom toolbar

hasClearFiltersBtn

  • Type: boolean
  • Default: true
  • Description: Show/hide the "Clear Filters" button in toolbar

hasClearSelectionBtn

  • Type: boolean
  • Default: true
  • Description: Show/hide the "Clear Selection" button in toolbar
  • Note: Only visible when disableSelect={false} or disableRows array has items

disableAllActions

  • Type: boolean
  • Default: false
  • Description: Hide all action buttons in the top toolbar

disableSideActions

  • Type: boolean
  • Default: false
  • Description: Hide side action buttons (export, cache, clear filters, help) while keeping main actions visible

gridHelper

  • Type: object | null
  • Default: null
  • Description: Adds a help button with custom content in a modal dialog
  • Required Properties (if defined):
    • title (string|element, required): Help dialog title
    • content (string|element, required): Help dialog content
  • Example:
gridHelper={{
  title: 'Grid Instructions',
  content: (
    <div>
      <h3>How to use this grid:</h3>
      <ul>
        <li>Use the filters above each column to search data</li>
        <li>Click rows to select them</li>
        <li>Use action buttons to perform operations</li>
        <li>Export data using the Excel or PDF buttons</li>
      </ul>
    </div>
  )
}}

Export Options

hasExcelExport

  • Type: boolean
  • Default: false
  • Description: Enable Excel export button in the toolbar

hasPdfExport

  • Type: boolean
  • Default: false
  • Description: Enable PDF export button in the toolbar

Export Behavior:

  • Both export buttons are rendered by the ExportActions component
  • Exports respect current filters and column visibility
  • Selected rows can be exported if selection is enabled
  • Export filename is auto-generated or can be customized

Selection & Interaction

disableSelect

  • Type: boolean
  • Default: false
  • Description: Completely disable row selection checkboxes and click-to-select behavior

enableMultiRowSelection

  • Type: boolean
  • Default: true
  • Description: Allow selecting multiple rows at once
  • Behavior:
    • true - Users can select multiple rows using checkboxes or Shift+Click
    • false - Only one row can be selected at a time

selectAllMode

  • Type: string
  • Default: 'page'
  • Options: 'page', 'all'
  • Description:
    • 'page' - "Select All" checkbox selects only rows on current page
    • 'all' - "Select All" checkbox selects all filtered rows across all pages

selectedIds

  • Type: Array
  • Default: []
  • Description: Array of row IDs that should be pre-selected when grid loads
  • Example:
selectedIds={[1, 5, 10]}  // Pre-select rows with these IDs
  • Note: Component syncs automatically when this prop changes

disableRows

  • Type: Array
  • Default: []
  • Description: Array of row IDs (based on rowKey) that should be disabled and cannot be selected
  • Example:
disableRows={[3, 7, 9]}  // Disable selection for these row IDs
  • Visual: Disabled rows are shown with gray background (#e3e3e3)

hasSubRows

  • Type: boolean
  • Default: false
  • Description: Enable support for hierarchical/nested rows
  • Requirements: Data must include subRows property for parent rows
  • Example Data:
const data = [
  {
    id: 1,
    name: 'Parent Row',
    subRows: [
      { id: '1.1', name: 'Child Row 1' },
      { id: '1.2', name: 'Child Row 2' }
    ]
  }
];

enableExpanding

  • Type: boolean
  • Default: false
  • Description: Show expand/collapse icons for rows with subRows
  • Requires: hasSubRows={true}

Pagination & Display

enablePagination

  • Type: boolean
  • Default: true
  • Description: Enable/disable pagination controls

pagination

  • Type: string
  • Default: 'both'
  • Options: 'top', 'bottom', 'both'
  • Description: Position of pagination controls
  • Note: When using 'top', pagination is integrated into the top toolbar

pageSize

  • Type: number
  • Default: 150
  • Description: Number of rows displayed per page initially

itemsPerPage

  • Type: Array<number>
  • Default: [50, 100, 150]
  • Description: Options available in the rows-per-page dropdown
  • Example:
itemsPerPage={[10, 25, 50, 100, 200]}

Styling & Layout

rowHeight

  • Type: number
  • Default: 75
  • Description: Minimum height of each row in pixels

fontSize

  • Type: number
  • Default: 14
  • Description: Base font size for grid content in pixels
  • Compact Mode: When enableCompactStyleMode={true}, responsive sizing is used: clamp(fontSize-3px, 1.1vw, fontSize)

gridHeight

  • Type: number | string
  • Default: 600
  • Description: Maximum height of the grid container
  • Examples:
gridHeight={600}              // 600px fixed height
gridHeight="80vh"             // 80% of viewport height
gridHeight="calc(100vh - 200px)" // Dynamic height calculation

enableCompactStyleMode

  • Type: boolean
  • Default: false
  • Description: Enable compact styling with reduced padding and responsive font sizes
  • Features:
    • Reduced cell padding (2px vs auto)
    • Responsive font sizing using CSS clamp()
    • Tighter filter input heights (17px vs default)
    • Reduced column header spacing
    • Optimized for displaying dense data

getRowStyle

  • Type: function
  • Description: Custom function to apply conditional styles to rows based on data
  • Signature: ({ row }) => object
  • Example:
getRowStyle={({ row }) => {
  const styles = {};
  
  if (row.original.status === 'active') {
    styles.backgroundColor = '#e8f5e9';
  }
  
  if (row.original.priority === 'high') {
    styles.color = 'red';
    styles.fontWeight = 'bold';
  }
  
  if (row.original.isExpired) {
    styles.opacity = 0.6;
    styles.textDecoration = 'line-through';
  }
  
  return styles;
}}

enableFixedHeader

  • Type: boolean
  • Default: true
  • Description: Pin column headers to top when scrolling vertically
  • Implementation: Uses CSS sticky positioning with class grid-sticky-header

enableFixedActions

  • Type: boolean
  • Default: false
  • Description: Pin action toolbar to top when scrolling
  • Requires: enableFixedHeader={true}
  • Implementation: Uses CSS sticky positioning with class grid-sticky-actions

Advanced Features

enableGlobalFilter

  • Type: boolean
  • Default: false
  • Description: Show global search input that filters across all columns

globalFilterFn

  • Type: string
  • Default: 'contains'
  • Options: 'contains', 'fuzzy', 'between', 'equals', 'greaterThan', 'lessThan', 'notEquals', 'lessThanOrEqualTo', 'greaterThanOrEqualTo', 'empty', 'notEmpty', 'startsWith', 'endsWith', 'betweenInclusive'
  • Description: Filter function used for global search

enableColumnFilterModes

  • Type: boolean
  • Default: true
  • Description: Allow users to change filter type per column (contains, equals, regex, etc.)
  • Available Modes: Rendered through ColumnFilter component

enableVirtualization

  • Type: boolean
  • Default: false
  • Description: Enable row and column virtualization for large datasets
  • Recommended: For grids with 1000+ rows
  • Configuration:
    • Row virtualizer: overscan: 5
    • Column virtualizer: overscan: columns.length
    • Scroll-to-top on sort change

enableFullScreenToggle

  • Type: boolean
  • Default: false
  • Description: Show fullscreen toggle button in toolbar

enableDensityToggle

  • Type: boolean
  • Default: false
  • Description: Show density toggle button (compact/comfortable/spacious)

Cache & Updates

updateCache

  • Type: function
  • Default: undefined
  • Description: Callback function triggered when cache update button is clicked
  • Signature: () => void
  • Example:
updateCache={() => {
  fetchLatestData();
}}
  • Note: Cache button only appears when this prop is provided

cacheUpdateText

  • Type: string
  • Default: undefined
  • Description: Tooltip text displayed on hover over the cache update button
  • Example: "Refresh data from server"

cacheUpdating

  • Type: boolean
  • Default: false
  • Description: Shows loading state on cache update button
  • Visual: Changes button icon to loading spinner
  • Disables: Button is disabled while true

Callbacks

onVisibleRowsChange

  • Type: function
  • Description: Called whenever the visible rows change (filtering, sorting, pagination)
  • Signature: (visibleRows: Array<Object>) => void
  • Example:
onVisibleRowsChange={(rows) => {
  console.log('Currently visible:', rows.length);
  updateDashboardStats(rows);
}}

Column Types

The DataGrid supports various pre-built cell types via the type property in column definitions. These formatters are automatically applied without requiring custom cell components.

Text Types

  • text - Plain text display
  • textTitle - Bold text (font-weight: bold)
  • textDescription - Gray text (#666666)
  • textSmall - Small font size text

Date Types

  • date - Date only (no time) - Format: locale-specific date
  • datetime - Date with time - Format: locale-specific datetime

Number Types

  • number - Raw number display
  • integer - Rounded integer (Math.round)
  • currency - EUR currency format (€1,234.56)
  • percentage - Percentage format (12.34%)

Link Types

  • email - Clickable mailto link (<a href="mailto:...">)
  • phone - Clickable tel link (<a href="tel:...">)
  • link - External link that opens in new tab (target="_blank")

Complex Types

  • array - Comma-separated array values
  • json - Formatted JSON display with indentation
  • countryFlag - Country flag image (requires ISO2 country code)
  • persons - List of persons with "et al." for 3+ people

Example with Types

const columns = [
  { accessorKey: 'name', header: 'Name', type: 'textTitle' },
  { accessorKey: 'description', header: 'Description', type: 'textDescription' },
  { accessorKey: 'email', header: 'Email', type: 'email' },
  { accessorKey: 'phone', header: 'Phone', type: 'phone' },
  { accessorKey: 'website', header: 'Website', type: 'link' },
  { accessorKey: 'salary', header: 'Salary', type: 'currency' },
  { accessorKey: 'completion', header: 'Progress', type: 'percentage' },
  { accessorKey: 'createdAt', header: 'Created', type: 'date', isDateColumn: true },
  { accessorKey: 'lastUpdated', header: 'Last Updated', type: 'datetime', isDateColumn: true },
  { accessorKey: 'country', header: 'Country', type: 'countryFlag' },
  { accessorKey: 'tags', header: 'Tags', type: 'array' },
  { accessorKey: 'metadata', header: 'Metadata', type: 'json' },
  { accessorKey: 'authors', header: 'Authors', type: 'persons' },
];

Custom Cell Renderers with Type Support

You have multiple options for cell rendering, each serving different use cases:

Option 1: Using Type Alone (Simplest)

Best for: Standard formatting without custom logic

const columns = [
  {
    accessorKey: 'price',
    header: 'Price',
    type: 'currency'  // Automatically formatted as €1,234.56
  }
];
// No custom Cell needed - formatter is applied automatically

Option 2: Type + cellClass

Best for: Adding CSS styling to formatted content

const columns = [
  {
    accessorKey: 'status',
    header: 'Status',
    type: 'text',
    cellClass: 'status-badge'  // Formatted text wrapped in <div class="status-badge">
  }
];

Option 3: Type + Custom Cell (Advanced)

Best for: Custom logic or styling while preserving type formatting

// Custom cell that uses the type formatter and adds custom styling
const CustomStatusCell = ({ typeValue, cell, row }) => {
  const rawValue = cell.getValue();
  const formattedValue = typeValue;  // Pre-formatted by type formatter
  
  return (
    <div style={{ 
      color: rawValue === 'active' ? 'green' : 'red',
      fontWeight: 'bold',
      padding: '4px 8px',
      borderRadius: '4px',
      backgroundColor: rawValue === 'active' ? '#e8f5e9' : '#ffebee'
    }}>
      {formattedValue || rawValue}  {/* Use formatted value or fallback */}
    </div>
  );
};

const columns = [
  {
    accessorKey: 'status',
    header: 'Status',
    type: 'text',              // Formatter is applied first
    Cell: CustomStatusCell,    // Custom component receives typeValue prop
  }
];

Option 4: Custom Cell Only (No Type)

Best for: Complete custom rendering without any formatter

const CustomCell = ({ cell, row }) => {
  const value = cell.getValue();
  
  return (
    <div className="custom-cell">
      <strong>{value}</strong>
      <span className="badge">{row.original.count}</span>
    </div>
  );
};

const columns = [
  {
    accessorKey: 'name',
    header: 'Name',
    Cell: CustomCell  // No type - full control over rendering
  }
];

Custom Cell Props Reference

When using a custom Cell component, you receive these props:

{
  cell: Object,           // MRT cell instance
  row: Object,            // MRT row instance
  table: Object,          // MRT table instance
  typeValue: any,         // Formatted value from type formatter (if type is defined)
  // Standard MRT props...
}

Important: Access row.original to get the original data object.


How Cell Rendering Works:

  1. accessorFn/accessorKey evaluated: Value is extracted from row data
  2. Type formatter applied: If type is specified, DEFAULT_CELL_TYPES[type] processes the value
  3. Content assigned: Formatted value becomes the base content
  4. Custom Cell receives props: If Cell is defined, it receives:
    • typeValue: The pre-formatted value
    • All standard MRT cell props
  5. CSS wrapper: If cellClass is defined, final content is wrapped in <div className={cellClass}>

Rendering Priority:

Raw Data → accessorFn → Type Formatter → typeValue → Custom Cell → cellClass Wrapper → Final Output

When to use each approach:

  • Use type only for standard formatting (95% of cases)
  • Use type + cellClass for simple styling needs
  • Use type + Cell when you need formatting AND custom logic
  • Use Cell only for completely custom rendering

Examples

Example 1: Basic Grid with Selection

import React, { useState } from 'react';
import DataGrid from '@insticc/react-datagrid-2';

function UserGrid() {
  const [selected, setSelected] = useState([]);

  const columns = [
    { accessorKey: 'id', header: 'ID', type: 'integer' },
    { accessorKey: 'name', header: 'Name', type: 'textTitle' },
    { accessorKey: 'email', header: 'Email', type: 'email' },
    { accessorKey: 'role', header: 'Role', type: 'text' },
    { accessorKey: 'createdAt', header: 'Joined', type: 'date', isDateColumn: true },
  ];

  const users = [
    { id: 1, name: 'John Doe', email: '[email protected]', role: 'Admin', createdAt: '2024-01-15' },
    { id: 2, name: 'Jane Smith', email: '[email protected]', role: 'User', createdAt: '2024-02-20' },
    { id: 3, name: 'Bob Johnson', email: '[email protected]', role: 'Manager', createdAt: '2024-03-10' },
  ];

  return (
    <DataGrid
      columns={columns}
      createRows={users}
      rowKey="id"
      selectData={setSelected}
      pageSize={50}
      itemsPerPage={[25, 50, 100]}
      gridHeight={500}
    />
  );
}

Example 2: Using accessorFn for Complex Data

function ConferenceGrid() {
  const [selected, setSelected] = useState([]);

  const columns = [
    { 
      accessorKey: 'acronym',
      accessorFn: (row) => row.acronym ?? '',  // Handle null/undefined
      header: 'Acronym',
      type: 'textTitle'
    },
    { 
      accessorKey: 'fullName',
      accessorFn: (row) => `${row.shortName || ''} - ${row.year || ''}`,  // Combine fields
      header: 'Conference Name',
      type: 'text'
    },
    { 
      accessorKey: 'papers',
      accessorFn: (row) => row.numberPapers?.value ?? 0,  // Nested object with fallback
      header: 'Papers',
      type: 'integer'
    },
    { 
      accessorKey: 'averageRating',
      accessorFn: (row) => row.ratings?.average ?? 0,  // Nested with default
      header: 'Avg Rating',
      type: 'number'
    },
    { 
      accessorKey: 'status',
      accessorFn: (row) => row.isActive ? 'Active' : 'Inactive',  // Conditional
      header: 'Status',
      type: 'text'
    },
    { 
      accessorKey: 'organizers',
      accessorFn: (row) => row.organizers?.map(o => o.name).join(', ') ?? 'N/A',  // Array processing
      header: 'Organizers',
      type: 'text'
    }
  ];

  const conferences = [
    {
      id: 1,
      acronym: 'ICEIS',
      shortName: 'International Conference on Enterprise Information Systems',
      year: 2025,
      numberPapers: { value: 150, trend: 'up' },
      ratings: { average: 4.5, count: 200 },
      isActive: true,
      organizers: [{ name: 'John Doe' }, { name: 'Jane Smith' }]
    },
    {
      id: 2,
      acronym: null,  // Will be handled by accessorFn
      shortName: 'Conference on AI',
      year: 2024,
      numberPapers: null,  // Will default to 0
      ratings: { average: 3.8, count: 50 },
      isActive: false,
      organizers: []
    }
  ];

  return (
    <DataGrid
      columns={columns}
      createRows={conferences}
      rowKey="id"
      selectData={setSelected}
      gridHeight={600}
    />
  );
}

Example 3: Grid with Actions and Exports

function ProductGrid() {
  const [selectedProducts, setSelectedProducts] = useState([]);
  const [products, setProducts] = useState(initialProducts);

  const handleDelete = (rows) => {
    const ids = rows.map(r => r.productId);
    if (window.confirm(`Delete ${ids.length} product(s)?`)) {
      setProducts(prev => prev.filter(p => !ids.includes(p.productId)));
    }
  };

  const handleEdit = (rows) => {
    if (rows.length === 1) {
      openEditModal(rows[0]);
    }
  };

  const handleAddNew = () => {
    openCreateModal();
  };

  const actions = [
    {
      name: 'Delete',
      function: handleDelete,
      tooltip: 'Delete selected products',
      color: 'red',
      icon: 'trash',
      selectionMode: 'multi',
      hasConfirmMessage: true,
      confirmMessage: 'Are you sure you want to delete the selected products?'
    },
    {
      name: 'Edit',
      function: handleEdit,
      tooltip: 'Edit product',
      color: 'blue',
      icon: 'edit',
      selectionMode: 'single'
    },
    {
      name: 'Add Product',
      function: handleAddNew,
      tooltip: 'Add new product',
      color: 'green',
      icon: 'plus',
      selectionMode: 'always'
    }
  ];

  const columns = [
    { accessorKey: 'productId', header: 'ID', type: 'integer' },
    { accessorKey: 'name', header: 'Product Name', type: 'textTitle' },
    { 
      accessorKey: 'price',
      accessorFn: (row) => row.price ?? 0,  // Ensure numeric value
      header: 'Price',
      type: 'currency'
    },
    { accessorKey: 'stock', header: 'Stock', type: 'integer' },
    { accessorKey: 'category', header: 'Category', type: 'text' },
  ];

  return (
    <DataGrid
      columns={columns}
      createRows={products}
      rowKey="productId"
      selectData={setSelectedProducts}
      actions={actions}
      hasExcelExport={true}
      hasPdfExport={true}
      gridHeight={600}
    />
  );
}

Example 3.5: Grid with Filter Actions

function TaskGrid() {
  const [tasks, setTasks] = useState(initialTasks);
  const [selected, setSelected] = useState([]);
  const [hideCompleted, setHideCompleted] = useState(false);
  const [hideArchived, setHideArchived] = useState(true);

  // Filter data based on filter states
  const filteredTasks = useMemo(() => {
    return tasks.filter(task => {
      if (hideCompleted && task.status === 'completed') return false;
      if (hideArchived && task.isArchived) return false;
      return true;
    });
  }, [tasks, hideCompleted, hideArchived]);

  const filterActions = [
    {
      name: 'Completed',
      state: hideCompleted,
      function: () => setHideCompleted(!hideCompleted),
      tooltip: hideCompleted ? 'Show completed tasks' : 'Hide completed tasks',
      key: 'filter-completed'
    },
    {
      name: 'Archived',
      state: hideArchived,
      function: () => setHideArchived(!hideArchived),
      tooltip: hideArchived ? 'Show archived tasks' : 'Hide archived tasks',
      key: 'filter-archived'
    },
    {
      // Example with dynamic name and conditional visibility
      state: hideUrgent,
      name: hideUrgent ? "Hiding Urgent" : "Showing Urgent",
      tooltip: hideUrgent ? "Show urgent tasks" : "Hide urgent tasks",
      function: () => setHideUrgent(!hideUrgent),
      visible: !!setHideUrgent,  // Only show if handler exists
    }
  ];

  const columns = [
    { accessorKey: 'id', header: 'ID', type: 'integer' },
    { accessorKey: 'title', header: 'Task', type: 'textTitle' },
    { accessorKey: 'status', header: 'Status', type: 'text' },
    { accessorKey: 'dueDate', header: 'Due Date', type: 'date', isDateColumn: true },
  ];

  return (
    <DataGrid
      columns={columns}
      createRows={filteredTasks}
      rowKey="id"
      selectData={setSelected}
      filterActions={filterActions}
      gridHeight={600}
    />
  );
}

Example 4: Hierarchical Data with SubRows

function ProjectGrid() {
  const [selectedItems, setSelectedItems] = useState([]);

  const columns = [
    { accessorKey: 'name', header: 'Name', type: 'textTitle' },
    { accessorKey: 'status', header: 'Status', type: 'text' },
    { accessorKey: 'assignee', header: 'Assignee', type: 'text' },
    { accessorKey: 'dueDate', header: 'Due Date', type: 'date', isDateColumn: true },
  ];

  const data = [
    {
      id: 1,
      name: 'Project Alpha',
      status: 'Active',
      assignee: 'John Doe',
      dueDate: '2025-06-30',
      subRows: [
        { id: '1.1', name: 'Task 1: Design', status: 'Complete', assignee: 'Jane', dueDate: '2025-02-15' },
        { id: '1.2', name: 'Task 2: Development', status: 'In Progress', assignee: 'Bob', dueDate: '2025-04-30' },
        { id: '1.3', name: 'Task 3: Testing', status: 'Not Started', assignee: 'Alice', dueDate: '2025-06-15' }
      ]
    },
    {
      id: 2,
      name: 'Project Beta',
      status: 'Planning',
      assignee: 'Jane Smith',
      dueDate: '2025-12-31',
      subRows: [
        { id: '2.1', name: 'Task 1: Requirements', status: 'In Progress', assignee: 'John', dueDate: '2025-03-01' }
      ]
    }
  ];

  return (
    <DataGrid
      columns={columns}
      createRows={data}
      rowKey="id"
      selectData={setSelectedItems}
      hasSubRows={true}
      enableExpanding={true}
      gridHeight={500}
    />
  );
}

Example 5: Custom Styling and Virtualization

function LargeDataGrid() {
  const [selectedRows, setSelectedRows] = useState([]);

  // Custom cell with type support
  const StatusCell = ({ typeValue, cell, row }) => {
    const value = cell.getValue();
    const getColor = (status) => {
      switch (status) {
        case 'active': return 'green';
        case 'pending': return 'orange';
        case 'error': return 'red';
        default: return 'grey';
      }
    };
    
    return (
      <span style={{ 
        color: getColor(value), 
        fontWeight: 'bold',
        padding: '2px 8px',
        borderRadius: '4px',
        backgroundColor: `${getColor(value)}22`
      }}>
        {typeValue || value}
      </span>
    );
  };

  const PriorityCell = ({ cell, row }) => {
    const priority = cell.getValue();
    const icons = {
      high: '🔴',
      medium: '🟡',
      low: '🟢'
    };
    
    return (
      <span>
        {icons[priority]} {priority.toUpperCase()}
      </span>
    );
  };

  const getRowStyle = ({ row }) => {
    const styles = {};
    
    if (row.original.status === 'error') {
      styles.backgroundColor = '#ffebee';
    }
    
    if (row.original.priority === 'high') {
      styles.borderLeft = '3px solid red';
    }
    
    if (row.original.isArchived) {
      styles.opacity = 0.5;
    }
    
    return styles;
  };

  const columns = [
    { accessorKey: 'id', header: 'ID', type: 'integer' },
    { 
      accessorKey: 'status', 
      header: 'Status', 
      type: 'text',
      Cell: StatusCell
    },
    {
      accessorKey: 'priority',
      header: 'Priority',
      Cell: PriorityCell
    },
    { accessorKey: 'name', header: 'Name', type: 'text' },
    { 
      accessorKey: 'value',
      accessorFn: (row) => row.value ?? 0,  // Ensure numeric value for currency
      header: 'Value',
      type: 'currency'
    },
    { 
      accessorKey: 'progress',
      accessorFn: (row) => row.progress ?? 0,  // Ensure numeric value for percentage
      header: 'Progress',
      type: 'percentage'
    },
    { accessorKey: 'updatedAt', header: 'Updated', type: 'datetime', isDateColumn: true },
  ];

  // Generate large dataset
  const largeDataset = Array.from({ length: 5000 }, (_, i) => ({
    id: i + 1,
    status: ['active', 'pending', 'error'][i % 3],
    priority: ['high', 'medium', 'low'][i % 3],
    name: `Item ${i + 1}`,
    value: Math.random() * 10000,
    progress: Math.random(),
    updatedAt: new Date(Date.now() - Math.random() * 365 * 24 * 60 * 60 * 1000).toISOString(),
    isArchived: i % 10 === 0
  }));

  return (
    <DataGrid
      columns={columns}
      createRows={largeDataset}
      rowKey="id"
      selectData={setSelectedRows}
      enableVirtualization={true}
      enableCompactStyleMode={true}
      rowHeight={40}
      fontSize={12}
      gridHeight="calc(100vh - 200px)"
      getRowStyle={getRowStyle}
      pageSize={100}
      itemsPerPage={[50, 100, 200, 500]}
    />
  );
}

Example 6: Advanced Features with Cache Management

function AdvancedGrid() {
  const [data, setData] = useState([]);
  const [cacheLoading, setCacheLoading] = useState(false);
  const [selectedRows, setSelectedRows] = useState([]);
  const [visibleRowCount, setVisibleRowCount] = useState(0);

  const refreshData = async () => {
    setCacheLoading(true);
    try {
      const newData = await fetchLatestData();
      setData(newData);
    } catch (error) {
      console.error('Failed to refresh data:', error);
    } finally {
      setCacheLoading(false);
    }
  };

  const handleVisibleRowsChange = (rows) => {
    setVisibleRowCount(rows.length);
    console.log('Visible rows:', rows);
  };

  const exportCustom = () => {
    console.log('Custom export logic');
  };

  const actions = [
    {
      name: 'Process',
      function: (rows) => processSelectedRows(rows),
      tooltip: 'Process selected items',
      color: 'blue',
      icon: 'cog',
      selectionMode: 'multi'
    }
  ];

  const extraActions = [
    {
      function: exportCustom,
      content: 'Custom Export',
      tooltip: 'Export with custom format',
      icon: 'file alternate outline'
    }
  ];

  const columns = [
    { accessorKey: 'id', header: 'ID', type: 'integer' },
    { accessorKey: 'name', header: 'Name', type: 'textTitle' },
    { accessorKey: 'status', header: 'Status', type: 'text' },
    { 
      accessorKey: 'amount',
      accessorFn: (row) => row.amount ?? 0,  // Ensure numeric value
      header: 'Amount',
      type: 'currency'
    },
    { accessorKey: 'createdAt', header: 'Created', type: 'datetime', isDateColumn: true },
  ];

  return (
    <DataGrid
      columns={columns}
      createRows={data}
      rowKey="id"
      selectData={setSelectedRows}
      
      // Actions
      actions={actions}
      extraActions={extraActions}
      
      // Advanced features
      enableGlobalFilter={true}
      globalFilterFn="fuzzy"
      enableColumnFilterModes={true}
      enableFixedHeader={true}
      enableFixedActions={true}
      
      // Cache management
      updateCache={refreshData}
      cacheUpdateText="Refresh data from server"
      cacheUpdating={cacheLoading}
      
      // Callbacks
      onVisibleRowsChange={handleVisibleRowsChange}
      
      // Exports
      hasExcelExport={true}
      hasPdfExport={true}
      
      // Help
      gridHelper={{
        title: 'Data Grid Help',
        content: (
          <div>
            <h3>Quick Guide</h3>
            <ul>
              <li><strong>Selection:</strong> Click rows to select, use checkboxes for multi-select</li>
              <li><strong>Filtering:</strong> Use column filters to search specific fields</li>
              <li><strong>Global Search:</strong> Search across all columns at once</li>
              <li><strong>Sorting:</strong> Click column headers to sort</li>
              <li><strong>Export:</strong> Use Excel or PDF buttons to export data</li>
              <li><strong>Refresh:</strong> Click cache button to reload latest data</li>
            </ul>
            <p><strong>Currently showing:</strong> {visibleRowCount} rows</p>
          </div>
        )
      }}
      
      // Display
      gridHeight={700}
      pageSize={100}
      itemsPerPage={[50, 100, 200]}
    />
  );
}

Example 7: Disabled Rows and Custom Row Styles

function OrderGrid() {
  const [orders, setOrders] = useState([]);
  const [selected, setSelected] = useState([]);

  // Disable completed and cancelled orders from selection
  const getDisabledRows = () => {
    return orders
      .filter(order => ['completed', 'cancelled'].includes(order.status))
      .map(order => order.orderId);
  };

  const getRowStyle = ({ row }) => {
    const status = row.original.status;
    
    switch (status) {
      case 'pending':
        return { backgroundColor: '#fff3cd' };
      case 'processing':
        return { backgroundColor: '#cfe2ff' };
      case 'completed':
        return { backgroundColor: '#d1e7dd', opacity: 0.7 };
      case 'cancelled':
        return { backgroundColor: '#f8d7da', opacity: 0.7 };
      default:
        return {};
    }
  };

  const columns = [
    { accessorKey: 'orderId', header: 'Order ID', type: 'text' },
    { accessorKey: 'customer', header: 'Customer', type: 'textTitle' },
    { 
      accessorKey: 'total',
      accessorFn: (row) => row.total ?? 0,  // Ensure numeric value
      header: 'Total',
      type: 'currency'
    },
    { accessorKey: 'status', header: 'Status', type: 'text' },
    { accessorKey: 'orderDate', header: 'Order Date', type: 'datetime', isDateColumn: true },
  ];

  return (
    <DataGrid
      columns={columns}
      createRows={orders}
      rowKey="orderId"
      selectData={setSelected}
      disableRows={getDisabledRows()}
      getRowStyle={getRowStyle}
      gridHeight={600}
    />
  );
}

API Reference

Column Definition Object

interface ColumnDefinition {
  // Required
  accessorKey: string;              // Key identifier for the column
  header: string;                   // Column header text
  
  // Data Access (choose one)
  // Option 1: Direct property access (default when no accessorFn)
  // Option 2: Custom accessor function
  accessorFn?: (row: Object) => any; // Function to extract/compute value from row
  
  // Type & Rendering
  type?: string;                    // Cell type for automatic formatting
  Cell?: React.Component;           // Custom cell renderer (receives typeValue)
  cellClass?: string;               // CSS class wrapper for cell content
  
  // Filtering
  enableColumnFilter?: boolean;     // Default: true
  enableColumnFilterModes?: boolean;// Default: true
  filterFn?: string | function;     // Default: 'contains' (or undefined for dates)
  
  // Sorting
  enableSorting?: boolean;          // Default: true
  sortingFn?: string | function;    // Default: 'basic' (or custom for dates)
  isDateColumn?: boolean;           // Special date handling (default: false)
  
  // Display
  enableResizing?: boolean;         // Default: true
  grow?: boolean;                   // Column can grow (default: true)
  enableClickToCopy?: boolean;      // Default: false
  enableColumnActions?: boolean;    // Default: false
  
  // Additional
  locale?: string;                  // Locale for formatting (e.g., 'en-US')
  onlyFlag?: boolean;               // For countryFlag type
  columnDef?: object;               // Additional metadata
}

Action Object

interface ActionObject {
  // Required
  name: string;                     // Button label
  function: (selectedRows: Array, table?: Object) => void; // Click handler
  
  // Display
  tooltip?: string;                 // Hover tooltip
  color?: 'blue' | 'red' | 'green' | 'yellow' | 'orange' | 'black' | 'grey' | 
          'teal' | 'brown' | 'violet' | 'purple' | 'olive' | 'pink';
  icon?: string | React.Element;    // Icon name or element
  
  // Behavior
  selectionMode?: 'single' | 'multi' | 'always';
  confirmMessage?: string | React.Element;
  hasConfirmMessage?: boolean;
  disabled?: boolean;
  visible?: boolean;                // Default: true
  
  // Toggle mode
  toggle?: boolean;
  active?: boolean;
  
  // Other
  key?: string;                     // Unique React key
}

Filter Action Object

interface FilterActionObject {
  // Required
  name: string;                     // Button label
  function: () => void;             // Toggle handler
  state: boolean;                   // Current filter state (true=hidden, false=shown)
  
  // Display
  tooltip?: string;                 // Hover tooltip
  icon?: string | React.Element;    // Icon name or element (default: eye/eye-slash based on state)
  visible?: boolean;                // Show/hide the button (default: true)
  
  // Behavior
  disabled?: boolean;               // Manually disable the button
  key?: string;                     // Unique React key (defaults to name)
}

Extra Action Object

interface ExtraActionObject {
  // Required
  function: () => void;             // Click handler
  content: string | React.Element;  // Button content/label
  
  // Display
  tooltip?: string;                 // Hover tooltip
  icon?: string | React.Element;    // Icon name or element
  style?: React.CSSProperties;      // Custom button styles
  propsButton?: object;             // Additional Semantic UI Button props
  visible?: boolean;                // Show/hide the button (default: true)
}

Data Row Object

interface DataRow {
  [rowKey: string]: any;            // Unique identifier (required)
  [key: string]: any;               // Other data fields
  
  // Optional: For hierarchical data
  subRows?: Array<DataRow>;
  isSubRow?: boolean;
}

Performance Tips

1. Use Virtualization for Large Datasets

// For 1000+ rows
enableVirtualization={true}

2. Optimize Row Height and Compact Mode

// Smaller rows = more visible data
rowHeight={40}
enableCompactStyleMode={true}
fontSize={12}

3. Disable Unused Features

// Reduce overhead by disabling features you don't need
enableDensityToggle={false}
enableFullScreenToggle={false}
enableGlobalFilter={false}
enableColumnFilterModes={false}

4. Limit Initial Page Size

// Don't render too many rows initially
pageSize={50}  // Instead of 150
itemsPerPage={[25, 50, 100]}

5. Pre-select Rows Carefully

// Only include necessary IDs
selectedIds={criticalIds}  // Instead of selecting all rows

6. Optimize Custom Cell Renderers

// Use typeValue when available
const MyCell = ({ typeValue, cell }) => {
  return typeValue || cell.getValue(); // Prefer pre-formatted typeValue
};

// Memoize expensive computations
const ExpensiveCell = React.memo(({ cell }) => {
  const processedValue = useMemo(() => expensiveComputation(cell.getValue()), [cell]);
  return <div>{processedValue}</div>;
});

7. Keep accessorFn Lightweight

// Bad - Complex computation on every render/filter/sort
accessorFn: (row) => {
  return expensiveCalculation(row.data);
}

// Good - Simple, fast operations
accessorFn: (row) => row.data?.value ?? 0

// Better - Pre-process data before passing to grid
const processedData = rawData.map(row => ({
  ...row,
  computedValue: expensiveCalculation(row.data)
}));

8. Use Column Visibility Wisely

// Hide columns that aren't immediately needed
columnVisibilityState={{
  metadata: false,
  internalId: false,
  debugInfo: false
}}

9. Implement Efficient Row Styling

// Keep getRowStyle lightweight
getRowStyle={({ row }) => {
  // Simple conditional - avoid heavy computation
  return row.original.isHighlighted ? { backgroundColor: '#fffacd' } : {};
}}

10. Batch State Updates

// Instead of multiple state updates, batch them
const handleMultipleChanges = () => {
  // Use a single state update with derived values
  setData(prevData => processAndUpdate(prevData));
};

11. Leverage React's Built-in Optimizations

// Use keys properly for subRows
const data = subRowData.map((item, index) => ({
  ...item,
  id: `${parentId}.${index}` // Stable, unique keys
}));

Browser Support

  • Chrome: Latest version (recommended)
  • Firefox: Latest version
  • Safari: Latest version
  • Edge: Latest version (Chromium-based)

CSS Requirements

  • CSS Grid support (all modern browsers)
  • CSS Sticky positioning for fixed headers
  • Flexbox for layouts

Troubleshooting

Common Issues

Issue: Rows not selecting when clicked

Symptoms: Clicking rows doesn't select them, checkboxes don't appear

Solutions:

  • Verify disableSelect={false} (this is the default)
  • Ensure selectData callback is provided
  • Check that rowKey matches your data's unique identifier property
  • Verify rows aren't in the disableRows array
// Correct configuration
<DataGrid
  rowKey="id"  // Matches data[0].id
  selectData={handleSelection}  // Required callback
  disableSelect={false}  // Optional - default is false
  disableRows={[]}  // No disabled rows
/>

Issue: Date columns not sorting correctly

Symptoms: Dates sort alphabetically instead of chronologically

Solutions:

  • Set isDateColumn={true} on date columns
  • Ensure date values are in valid ISO 8601 format (YYYY-MM-DD or YYYY-MM-DDTHH:mm:ss)
  • Check that date strings are parseable by JavaScript Date constructor
// Correct date column configuration
{
  accessorKey: 'createdAt',
  header: 'Created Date',
  type: 'date',
  isDateColumn: true  // Enables date-aware sorting
}

// Data format
const data = [
  { id: 1, createdAt: '2025-01-15' },  // ISO 8601 format
  { id: 2, createdAt: '2024-12-20' },
];

Issue: Fixed headers not working

Symptoms: Headers scroll with content instead of staying fixed

Solutions:

  • Set enableFixedHeader={true} (default is true)
  • Ensure gridHeight is set to a specific numeric value or CSS string
  • Do not use gridHeight="fit-content" with fixed headers
  • Check for CSS conflicts with grid-sticky-header class
// Correct fixed header configuration
<DataGrid
  enableFixedHeader={true}
  gridHeight={600}  // Or "80vh", not "fit-content"
/>

Issue: Fixed actions toolbar not sticking

Symptoms: Action buttons scroll instead of staying at top

Solutions:

  • Enable both enableFixedHeader={true} and enableFixedActions={true}
  • Verify gridHeight is set to a specific value
  • Check for CSS conflicts with grid-sticky-actions class
<DataGrid
  enableFixedHeader={true}
  enableFixedActions={true}
  gridHeight={700}
/>

Issue: Selection state not syncing with parent component

Symptoms: External state doesn't match grid selection

Solutions:

  • Use the selectedIds prop to control selection from parent
  • Component automatically syncs when selectedIds prop changes
  • Ensure rowKey values in selectedIds match data
const [externalSelection, setExternalSelection] = useState([1, 5, 10]);

<DataGrid
  selectedIds={externalSelection}  // Controlled selection
  selectData={(rows) => {
    const ids = rows.map(r => r.id);
    setExternalSelection(ids);
  }}
/>

Issue: Export buttons not appearing

Symptoms: Excel/PDF export buttons are not visible

Solutions:

  • Set hasExcelExport={true} and/or hasPdfExport={true}
  • Check that disableSideActions={false} (default)
  • Ensure toolbar has space (not too many actions)
  • Verify enableTopToolbar={true} (default)
<DataGrid
  hasExcelExport={true}
  hasPdfExport={true}
  enableTopToolbar={true}
  disableSideActions={false}
/>

Issue: Type formatter not working

Symptoms: Column values appear as raw data instead of formatted

Solutions:

  • Verify column has type property defined
  • Check that type value exists in DEFAULT_CELL_TYPES (e.g., 'text', 'date', 'currency', 'email')
  • Ensure data values are in correct format for the type
  • Check browser console for errors
// Correct type configuration
{
  accessorKey: 'price',
  header: 'Price',
  type: 'currency'  // Must match a valid type
}

Issue: Custom Cell component not receiving typeValue

Symptoms: typeValue prop is undefined in custom Cell

Solutions:

  • Verify column has both type AND Cell defined
  • Check that type matches a key in DEFAULT_CELL_TYPES
  • typeValue only exists when type formatter is applied
// Correct configuration for typeValue
const CustomCell = ({ typeValue, cell }) => {
  console.log(typeValue);  // Will be defined
  return <div>{typeValue}</div>;
};

{
  accessorKey: 'amount',
  type: 'currency',  // Required for typeValue
  Cell: CustomCell   // Will receive typeValue
}

Issue: accessorFn not being used for filtering/sorting

Symptoms: Filters and sorts use raw data instead of accessorFn result

Solutions:

  • Verify accessorFn is defined correctly
  • The returned value from accessorFn is what gets filtered/sorted
  • For complex objects, ensure accessorFn returns a simple value
// Bad - Returns object (hard to filter/sort)
accessorFn: (row) => ({ value: row.data })

// Good - Returns simple value
accessorFn: (row) => row.data?.value ?? 0

Issue: Virtualization causing scroll issues

Symptoms: Jerky scrolling, rows not rendering, blank spaces

Solutions:

  • Ensure rowHeight is set accurately
  • Don't use dynamic row heights with virtualization
  • Check that enableVirtualization={true} is actually needed (only for 1000+ rows)
  • Verify data doesn't change reference on every render
// Good for virtualization
<DataGrid
  enableVirtualization={true}
  rowHeight={40}  // Fixed height
  createRows={stableDataReference}  // Don't recreate array
/>

Issue: Actions not enabling/disabling correctly

Symptoms: Action buttons stay disabled when rows are selected

Solutions:

  • Check selectionMode setting ('single', 'multi', 'always')
  • Verify disabled prop isn't set to true
  • Ensure rows are actually being selected (check selectData callback)
actions={[
  {
    name: 'Edit',
    selectionMode: 'single',  // Requires exactly 1 row
    function: handleEdit,
    disabled: false  // Not manually disabled
  }
]}

Issue: Hierarchical data (subRows) not working

Symptoms: SubRows don't expand or aren't selectable

Solutions:

  • Set hasSubRows={true}
  • Set enableExpanding={true} to show expand icons
  • Ensure data has correct subRows structure
  • SubRow IDs should follow pattern: "parentId.index"
const hierarchicalData = [
  {
    id: 1,
    name: 'Parent',
    subRows: [
      { id: '1.1', name: 'Child 1' },
      { id: '1.2', name: 'Child 2' }
    ]
  }
];

<DataGrid
  hasSubRows={true}
  enableExpanding={true}
  createRows={hierarchicalData}
/>

Issue: Cache update button not appearing

Symptoms: Cache/refresh button is not visible

Solutions:

  • Provide updateCache function prop
  • Optionally set cacheUpdateText for tooltip
  • Button only appears when updateCache is defined
<DataGrid
  updateCache={() => fetchLatestData()}
  cacheUpdateText="Refresh from server"
  cacheUpdating={isLoading}
/>

Issue: Row styles not applying

Symptoms: getRowStyle returns styles but they don't appear

Solutions:

  • Check that returned object contains valid CSS properties
  • Verify function is actually being called
  • CSS specificity might be overriding - use !important if needed
  • Check browser console for CSS errors
getRowStyle={({ row }) => {
  console.log('Styling row:', row.id);  // Debug
  return {
    backgroundColor: row.original.isHighlighted ? '#fffacd !important' : 'white'
  };
}}

Issue: Filters not working

Symptoms: Column filters don't filter data

Solutions:

  • Verify enableColumnFilter={true} on columns (default)
  • Check that filterFn is valid
  • Ensure data values match filter input type
  • If using accessorFn, ensure it returns filterable values
{
  accessorKey: 'status',
  header: 'Status',
  enableColumnFilter: true,
  filterFn: 'contains'  // or 'equals', 'startsWith', etc.
}

// With accessorFn
{
  accessorKey: 'papers',
  accessorFn: (row) => row.numberPapers?.value ?? 0,  // Returns number for filtering
  header: 'Papers',
  type: 'integer'
}

Important Notes

Row Selection Behavior

  • Disabled Select: When disableSelect={true} hide the selection checkbox/radio and cannot be selected
  • Single selection mode: When enableMultiRowSelection={false}, clicking a row deselects all others
  • Multi-selection: Use checkboxes when enableMultiRowSelection={true}
  • Selection state: Controlled via rowSelection state and selectData callback

Fixed Header and Actions

  • enableFixedHeader uses CSS position: sticky on column headers
  • enableFixedActions (requires enableFixedHeader={true}) also pins the toolbar
  • Both require gridHeight to be set to a specific value (not 'fit-content')
  • CSS classes: grid-sticky-header and grid-sticky-actions

Date Columns

  • Use type: 'date' for date-only display (no time)
  • Use type: 'datetime' for full datetime display
  • Default locale is 'pt-PT' - override with locale prop on column
  • Date values should be ISO 8601 format for best results

accessorFn Performance

  • accessorFn is called for every cell render, filter operation, and sort operation
  • Keep computations lightweight
  • Avoid creating new objects/arrays in accessorFn
  • Pre-process complex data before passing to the grid when possible
  • The returned value is what gets filtered and sorted

LocalizationProvider

  • Component is wrapped with MUI's LocalizationProvider using AdapterDateFns
  • Required for date picker functionality in filters
  • No need to add this provider in your own code

SubRows and Hierarchical Data

  • SubRow IDs follow pattern: "parentId.subRowIndex" (e.g., "1.0", "1.1")
  • Selection works on both parent and child rows
  • Visual depth indicated by background color and padding
  • Requires hasSubRows={true} to enable

Performance Considerations

  • Virtualization is recommended for 1000+ rows
  • Compact style mode reduces render overhead
  • Custom cell components should be memoized when possible
  • Avoid recreating data arrays on every render
  • Keep accessorFn computations lightweight

Browser Storage

  • Component does NOT use localStorage or sessionStorage
  • All state is maintained in React state (useState)
  • Selection state is ephemeral unless managed by parent

Dependencies

This component requires the following peer dependencies:

Core Dependencies

  • react (^16.8.0 || ^17.0.0 || ^18.0.0) - React framework
  • prop-types - Runtime type checking for React props
  • material-react-table - Core table functionality and hooks
  • @mui/material - Material UI components
  • @mui/x-date-pickers - Date picker components for filters
  • date-fns - Date manipulation and formatting
  • semantic-ui-react - Semantic UI React components for buttons and icons
  • bootstrap - Base CSS framework
  • semantic-ui-css - Semantic UI CSS styles

Version Compatibility

{
  "peerDependencies": {
    "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
    "material-react-table": "^2.0.0",
    "@mui/material": "^5.0.0",
    "semantic-ui-react": "^2.0.0"
  }
}

Built With


License

ISC