DEV Community

Michael Turner
Michael Turner

Posted on

Building Interactive Data Tables with React Data Grid

eact Data Grid (by Adazzle) is a powerful, feature-rich data grid component for React that provides Excel-like functionality with virtualization, sorting, filtering, and cell editing. It's designed to handle large datasets efficiently while maintaining smooth performance. This guide walks through creating interactive, performant data tables using React Data Grid, covering setup, configuration, and practical implementation patterns. This is part 5 of a series on using React Data Grid with React.

Prerequisites

Before you begin, ensure you have:

  • Node.js version 16.0 or higher
  • npm, yarn, or pnpm package manager
  • A React project (version 16.8 or higher) with hooks support
  • Basic understanding of React hooks (useState, useCallback)
  • Familiarity with TypeScript (optional but recommended)

Installation

Install React Data Grid using your preferred package manager:

npm install react-data-grid
Enter fullscreen mode Exit fullscreen mode

Or with yarn:

yarn add react-data-grid
Enter fullscreen mode Exit fullscreen mode

Or with pnpm:

pnpm add react-data-grid
Enter fullscreen mode Exit fullscreen mode

For TypeScript support, types are included in the package. Your package.json should include:

{
  "dependencies": {
    "react-data-grid": "^7.0.0",
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

Project Setup

React Data Grid requires CSS styles to be imported. Add the styles to your main application file or component:

// src/index.js or src/App.jsx
import 'react-data-grid/lib/styles.css';
Enter fullscreen mode Exit fullscreen mode

If you're using a module bundler that doesn't handle CSS imports, you may need to configure it or import the CSS in your HTML file.

First Example / Basic Usage

Let's create a basic data grid component. Create src/DataTable.jsx:

// src/DataTable.jsx
import React from 'react';
import { DataGrid } from 'react-data-grid';
import 'react-data-grid/lib/styles.css';

// Define the row type
interface Row {
  id: number;
  title: string;
  count: number;
  status: string;
}

// Define columns
const columns = [
  { key: 'id', name: 'ID', width: 80 },
  { key: 'title', name: 'Title', resizable: true },
  { key: 'count', name: 'Count', width: 100 },
  { key: 'status', name: 'Status', width: 120 }
];

// Sample data
const rows: Row[] = [
  { id: 0, title: 'Task 1', count: 10, status: 'Active' },
  { id: 1, title: 'Task 2', count: 5, status: 'Pending' },
  { id: 2, title: 'Task 3', count: 15, status: 'Active' },
  { id: 3, title: 'Task 4', count: 8, status: 'Completed' },
  { id: 4, title: 'Task 5', count: 20, status: 'Active' }
];

function DataTable() {
  return (
    <div style={{ height: 400, width: '100%' }}>
      <DataGrid columns={columns} rows={rows} />
    </div>
  );
}

export default DataTable;
Enter fullscreen mode Exit fullscreen mode

If you're not using TypeScript, you can use JavaScript:

// src/DataTable.jsx
import React from 'react';
import { DataGrid } from 'react-data-grid';
import 'react-data-grid/lib/styles.css';

const columns = [
  { key: 'id', name: 'ID', width: 80 },
  { key: 'title', name: 'Title', resizable: true },
  { key: 'count', name: 'Count', width: 100 },
  { key: 'status', name: 'Status', width: 120 }
];

const rows = [
  { id: 0, title: 'Task 1', count: 10, status: 'Active' },
  { id: 1, title: 'Task 2', count: 5, status: 'Pending' },
  { id: 2, title: 'Task 3', count: 15, status: 'Active' },
  { id: 3, title: 'Task 4', count: 8, status: 'Completed' },
  { id: 4, title: 'Task 5', count: 20, status: 'Active' }
];

function DataTable() {
  return (
    <div style={{ height: 400, width: '100%' }}>
      <DataGrid columns={columns} rows={rows} />
    </div>
  );
}

export default DataTable;
Enter fullscreen mode Exit fullscreen mode

Update your App.jsx:

// src/App.jsx
import React from 'react';
import DataTable from './DataTable';
import './App.css';

function App() {
  return (
    <div className="App" style={{ padding: '20px' }}>
      <h1>React Data Grid Example</h1>
      <DataTable />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

This creates a basic data grid with resizable columns. You can drag column borders to resize them.

Understanding the Basics

React Data Grid uses a column-based configuration where each column defines:

  • key: Unique identifier that maps to a property in your row objects
  • name: Display header text
  • width: Column width in pixels (optional)
  • resizable: Allow column resizing (default: false)
  • sortable: Enable sorting (default: false)
  • editable: Enable cell editing (default: false)

Key concepts:

  • Columns: Define the structure and behavior of each column
  • Rows: Array of data objects where each object represents a table row
  • Virtualization: Automatically handles large datasets efficiently
  • Row Key Getter: Function to provide unique identifiers for rows (important for selection and updates)

Here's an example with sorting and row selection:

// src/SortableTable.jsx
import React, { useState } from 'react';
import { DataGrid, SelectColumn } from 'react-data-grid';
import 'react-data-grid/lib/styles.css';

const columns = [
  SelectColumn,
  { key: 'id', name: 'ID', width: 80, sortable: true },
  { key: 'name', name: 'Name', width: 200, sortable: true, resizable: true },
  { key: 'email', name: 'Email', width: 250, sortable: true },
  { key: 'role', name: 'Role', width: 150, sortable: true }
];

const rows = [
  { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin' },
  { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
  { id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'User' },
  { id: 4, name: 'Alice Williams', email: 'alice@example.com', role: 'Admin' }
];

function SortableTable() {
  const [sortColumns, setSortColumns] = useState([]);
  const [selectedRows, setSelectedRows] = useState(new Set());

  const sortedRows = React.useMemo(() => {
    if (sortColumns.length === 0) return rows;

    return [...rows].sort((a, b) => {
      for (const sort of sortColumns) {
        const aValue = a[sort.columnKey];
        const bValue = b[sort.columnKey];
        const comp = aValue < bValue ? -1 : aValue > bValue ? 1 : 0;
        return sort.direction === 'ASC' ? comp : -comp;
      }
      return 0;
    });
  }, [sortColumns]);

  return (
    <div style={{ height: 400, width: '100%' }}>
      <DataGrid
        columns={columns}
        rows={sortedRows}
        rowKeyGetter={(row) => row.id}
        selectedRows={selectedRows}
        onSelectedRowsChange={setSelectedRows}
        sortColumns={sortColumns}
        onSortColumnsChange={setSortColumns}
      />
    </div>
  );
}

export default SortableTable;
Enter fullscreen mode Exit fullscreen mode

Practical Example / Building Something Real

Let's build a comprehensive task management table with editing, filtering, and custom rendering:

// src/TaskManager.jsx
import React, { useState, useMemo } from 'react';
import { DataGrid } from 'react-data-grid';
import 'react-data-grid/lib/styles.css';

const columns = [
  { 
    key: 'id', 
    name: 'ID', 
    width: 60,
    frozen: true
  },
  { 
    key: 'title', 
    name: 'Title', 
    width: 200,
    resizable: true,
    editable: true,
    sortable: true
  },
  { 
    key: 'priority', 
    name: 'Priority', 
    width: 120,
    editable: true,
    renderCell: ({ row }) => {
      const colors = {
        High: '#dc3545',
        Medium: '#ffc107',
        Low: '#28a745'
      };
      return (
        <span style={{ 
          color: colors[row.priority] || '#6c757d',
          fontWeight: 'bold'
        }}>
          {row.priority}
        </span>
      );
    },
    renderEditCell: ({ row, onRowChange }) => {
      return (
        <select
          value={row.priority}
          onChange={(e) => onRowChange({ ...row, priority: e.target.value })}
          style={{ width: '100%', padding: '4px' }}
        >
          <option value="High">High</option>
          <option value="Medium">Medium</option>
          <option value="Low">Low</option>
        </select>
      );
    }
  },
  { 
    key: 'status', 
    name: 'Status', 
    width: 120,
    editable: true,
    renderCell: ({ row }) => {
      const statusColors = {
        'To Do': '#6c757d',
        'In Progress': '#007bff',
        'Done': '#28a745'
      };
      return (
        <span style={{ 
          color: statusColors[row.status] || '#6c757d',
          fontWeight: 'bold'
        }}>
          {row.status}
        </span>
      );
    }
  },
  { 
    key: 'assignee', 
    name: 'Assignee', 
    width: 150,
    editable: true,
    sortable: true
  },
  { 
    key: 'dueDate', 
    name: 'Due Date', 
    width: 120,
    editable: true,
    renderCell: ({ row }) => {
      if (!row.dueDate) return '-';
      const date = new Date(row.dueDate);
      return date.toLocaleDateString();
    },
    renderEditCell: ({ row, onRowChange }) => {
      return (
        <input
          type="date"
          value={row.dueDate || ''}
          onChange={(e) => onRowChange({ ...row, dueDate: e.target.value })}
          style={{ width: '100%', padding: '4px' }}
        />
      );
    }
  }
];

const initialRows = [
  { id: 1, title: 'Implement user authentication', priority: 'High', status: 'In Progress', assignee: 'John Doe', dueDate: '2024-12-31' },
  { id: 2, title: 'Design database schema', priority: 'Medium', status: 'Done', assignee: 'Jane Smith', dueDate: '2024-12-15' },
  { id: 3, title: 'Write API documentation', priority: 'Low', status: 'To Do', assignee: 'Bob Johnson', dueDate: '2025-01-10' },
  { id: 4, title: 'Setup CI/CD pipeline', priority: 'High', status: 'In Progress', assignee: 'Alice Williams', dueDate: '2024-12-20' },
  { id: 5, title: 'Code review and testing', priority: 'Medium', status: 'To Do', assignee: 'John Doe', dueDate: '2025-01-05' }
];

function TaskManager() {
  const [rows, setRows] = useState(initialRows);
  const [sortColumns, setSortColumns] = useState([]);
  const [filter, setFilter] = useState('');

  const filteredRows = useMemo(() => {
    if (!filter) return rows;
    const lowerFilter = filter.toLowerCase();
    return rows.filter(row => 
      Object.values(row).some(value => 
        String(value).toLowerCase().includes(lowerFilter)
      )
    );
  }, [rows, filter]);

  const sortedRows = useMemo(() => {
    if (sortColumns.length === 0) return filteredRows;

    return [...filteredRows].sort((a, b) => {
      for (const sort of sortColumns) {
        const aValue = a[sort.columnKey];
        const bValue = b[sort.columnKey];
        const comp = aValue < bValue ? -1 : aValue > bValue ? 1 : 0;
        return sort.direction === 'ASC' ? comp : -comp;
      }
      return 0;
    });
  }, [filteredRows, sortColumns]);

  const handleRowChange = (newRow) => {
    setRows(rows.map(row => row.id === newRow.id ? newRow : row));
  };

  return (
    <div style={{ padding: '20px' }}>
      <h2>Task Management</h2>
      <div style={{ marginBottom: '16px' }}>
        <input
          type="text"
          placeholder="Search tasks..."
          value={filter}
          onChange={(e) => setFilter(e.target.value)}
          style={{
            padding: '8px',
            width: '300px',
            border: '1px solid #ddd',
            borderRadius: '4px'
          }}
        />
      </div>
      <div style={{ height: 500, width: '100%', border: '1px solid #ddd' }}>
        <DataGrid
          columns={columns}
          rows={sortedRows}
          rowKeyGetter={(row) => row.id}
          onRowsChange={setRows}
          sortColumns={sortColumns}
          onSortColumnsChange={setSortColumns}
          defaultColumnOptions={{
            resizable: true,
            sortable: true
          }}
        />
      </div>
    </div>
  );
}

export default TaskManager;
Enter fullscreen mode Exit fullscreen mode

This example demonstrates:

  • Editable cells with custom editors (dropdown, date picker)
  • Custom cell rendering with conditional styling
  • Frozen columns (ID column stays fixed)
  • Sorting functionality
  • Global search/filtering
  • Row updates with state management
  • Resizable columns

Common Issues / Troubleshooting

  1. Styles not loading: Make sure you've imported 'react-data-grid/lib/styles.css' in your component or main entry file. Without styles, the grid won't display correctly.

  2. Row selection not working: Implement rowKeyGetter prop to provide unique identifiers for each row. This is required for selection features.

  3. Sorting not working: Ensure you're managing sortColumns state and implementing the sorting logic yourself, or use a library that handles it. React Data Grid doesn't sort automatically.

  4. Performance with large datasets: React Data Grid uses virtualization by default, but for very large datasets (10,000+ rows), consider implementing server-side pagination or filtering.

  5. TypeScript errors: Make sure you're using the correct types from react-data-grid. The library exports TypeScript types, so ensure your column and row types match the expected interfaces.

Next Steps

Now that you understand React Data Grid basics:

  • Explore advanced features like row grouping and aggregation
  • Implement custom cell editors and renderers
  • Add row expansion for nested data
  • Learn about column pinning and reordering
  • Implement server-side data loading
  • Add export functionality (CSV, Excel)
  • Check the official repository: https://github.com/adazzle/react-data-grid
  • Look for part 6 of this series for more advanced topics

Summary

You've learned how to set up React Data Grid and create interactive data tables with sorting, filtering, editing, and custom rendering. The library provides excellent performance for large datasets through virtualization and offers extensive customization options for building sophisticated data management interfaces.

Top comments (0)