DEV Community

Michael Turner
Michael Turner

Posted on

Building Advanced Data Tables with AG Grid in React

AG Grid is a powerful, feature-rich data grid library for React that provides enterprise-grade functionality including sorting, filtering, pagination, cell editing, and much more. It's designed to handle large datasets efficiently while maintaining excellent performance. This guide walks through creating advanced, interactive data tables using AG Grid with React, covering setup, configuration, and practical implementation patterns. This is part 2 of a series on using AG 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, useRef, useCallback)
  • Familiarity with JavaScript/TypeScript

Installation

Install AG Grid React package and its community styles:

npm install ag-grid-react ag-grid-community
Enter fullscreen mode Exit fullscreen mode

Or with yarn:

yarn add ag-grid-react ag-grid-community
Enter fullscreen mode Exit fullscreen mode

Or with pnpm:

pnpm add ag-grid-react ag-grid-community
Enter fullscreen mode Exit fullscreen mode

Your package.json should now include:

{
  "dependencies": {
    "ag-grid-react": "^31.0.0",
    "ag-grid-community": "^31.0.0",
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

Project Setup

AG Grid requires CSS styles to be imported. Add the grid styles and a theme to your main application file:

// src/index.js or src/main.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-quartz.css';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
Enter fullscreen mode Exit fullscreen mode

Available themes include:

  • ag-theme-quartz (modern, default)
  • ag-theme-alpine (classic)
  • ag-theme-balham (dark)

First Example / Basic Usage

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

// src/DataGrid.jsx
import React, { useState } from 'react';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-quartz.css';

function DataGrid() {
  // Row data - the data to be displayed
  const [rowData] = useState([
    { make: 'Tesla', model: 'Model Y', price: 64950, electric: true },
    { make: 'Ford', model: 'F-Series', price: 33850, electric: false },
    { make: 'Toyota', model: 'Corolla', price: 29600, electric: false },
    { make: 'BMW', model: '3 Series', price: 41500, electric: false },
    { make: 'Nissan', model: 'Leaf', price: 28140, electric: true }
  ]);

  // Column definitions - defines the columns structure
  const [columnDefs] = useState([
    { field: 'make', sortable: true, filter: true },
    { field: 'model', sortable: true, filter: true },
    { 
      field: 'price', 
      sortable: true, 
      filter: 'agNumberColumnFilter',
      valueFormatter: params => '$' + params.value.toLocaleString()
    },
    { 
      field: 'electric',
      cellRenderer: params => params.value ? '⚡ Yes' : 'No'
    }
  ]);

  // Default column properties applied to all columns
  const defaultColDef = {
    sortable: true,
    filter: true,
    flex: 1,
    resizable: true
  };

  return (
    <div className="ag-theme-quartz" style={{ height: 500, width: '100%' }}>
      <AgGridReact
        rowData={rowData}
        columnDefs={columnDefs}
        defaultColDef={defaultColDef}
        pagination={true}
        paginationPageSize={10}
      />
    </div>
  );
}

export default DataGrid;
Enter fullscreen mode Exit fullscreen mode

Update your App.jsx:

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

function App() {
  return (
    <div className="App">
      <h1>AG Grid Data Table</h1>
      <DataGrid />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

This creates a fully functional data grid with sorting, filtering, and pagination. Users can click column headers to sort, use the filter icons, and navigate through pages.

Understanding the Basics

AG Grid uses a declarative approach where you define:

  • rowData: An array of objects representing table rows
  • columnDefs: An array defining column structure, properties, and behavior
  • defaultColDef: Default properties applied to all columns

Key concepts:

  • Field: Maps to a property in your row data object
  • Cell Renderer: Custom component or function to render cell content
  • Value Formatter: Formats the displayed value without changing the data
  • Filters: Built-in filters (text, number, date) or custom filters
  • Sorting: Enable sorting per column or globally

Here's an example with editable cells and row selection:

// src/EditableGrid.jsx
import React, { useState, useCallback } from 'react';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-quartz.css';

function EditableGrid() {
  const [rowData, setRowData] = useState([
    { id: 1, name: 'John Doe', email: 'john@example.com', age: 28 },
    { id: 2, name: 'Jane Smith', email: 'jane@example.com', age: 32 },
    { id: 3, name: 'Bob Johnson', email: 'bob@example.com', age: 45 }
  ]);

  const [columnDefs] = useState([
    { 
      field: 'id', 
      checkboxSelection: true, 
      headerCheckboxSelection: true,
      width: 100
    },
    { field: 'name', editable: true, sortable: true },
    { field: 'email', editable: true, filter: 'agTextColumnFilter' },
    { 
      field: 'age', 
      editable: true, 
      filter: 'agNumberColumnFilter',
      cellEditor: 'agNumberCellEditor'
    }
  ]);

  const defaultColDef = {
    flex: 1,
    sortable: true,
    filter: true
  };

  const onCellValueChanged = useCallback((event) => {
    console.log('Cell value changed:', event.data);
    // Update your state or sync with backend
    const updatedData = rowData.map(row => 
      row.id === event.data.id ? event.data : row
    );
    setRowData(updatedData);
  }, [rowData]);

  return (
    <div className="ag-theme-quartz" style={{ height: 400, width: '100%' }}>
      <AgGridReact
        rowData={rowData}
        columnDefs={columnDefs}
        defaultColDef={defaultColDef}
        rowSelection="multiple"
        onCellValueChanged={onCellValueChanged}
        animateRows={true}
      />
    </div>
  );
}

export default EditableGrid;
Enter fullscreen mode Exit fullscreen mode

Practical Example / Building Something Real

Let's build a comprehensive employee management table with advanced features:

// src/EmployeeTable.jsx
import React, { useState, useRef, useCallback } from 'react';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-quartz.css';

function EmployeeTable() {
  const gridRef = useRef();
  const [rowData] = useState([
    { 
      id: 1, 
      name: 'Sarah Johnson', 
      department: 'Engineering', 
      salary: 95000, 
      startDate: '2020-01-15',
      status: 'Active'
    },
    { 
      id: 2, 
      name: 'Michael Chen', 
      department: 'Marketing', 
      salary: 75000, 
      startDate: '2019-06-20',
      status: 'Active'
    },
    { 
      id: 3, 
      name: 'Emily Davis', 
      department: 'Sales', 
      salary: 65000, 
      startDate: '2021-03-10',
      status: 'Active'
    },
    { 
      id: 4, 
      name: 'David Wilson', 
      department: 'Engineering', 
      salary: 110000, 
      startDate: '2018-09-05',
      status: 'Active'
    },
    { 
      id: 5, 
      name: 'Lisa Anderson', 
      department: 'HR', 
      salary: 70000, 
      startDate: '2022-01-08',
      status: 'Active'
    }
  ]);

  const [columnDefs] = useState([
    { 
      field: 'id', 
      headerName: 'ID',
      width: 80,
      checkboxSelection: true,
      headerCheckboxSelection: true
    },
    { 
      field: 'name', 
      headerName: 'Employee Name',
      sortable: true,
      filter: 'agTextColumnFilter',
      editable: true
    },
    { 
      field: 'department', 
      headerName: 'Department',
      sortable: true,
      filter: 'agSetColumnFilter',
      editable: true,
      cellEditor: 'agSelectCellEditor',
      cellEditorParams: {
        values: ['Engineering', 'Marketing', 'Sales', 'HR', 'Finance']
      }
    },
    { 
      field: 'salary', 
      headerName: 'Salary',
      sortable: true,
      filter: 'agNumberColumnFilter',
      editable: true,
      cellEditor: 'agNumberCellEditor',
      valueFormatter: params => '$' + params.value.toLocaleString(),
      cellStyle: params => {
        if (params.value > 100000) {
          return { backgroundColor: '#d4edda' };
        }
        return null;
      }
    },
    { 
      field: 'startDate', 
      headerName: 'Start Date',
      sortable: true,
      filter: 'agDateColumnFilter',
      valueFormatter: params => {
        const date = new Date(params.value);
        return date.toLocaleDateString('en-US');
      }
    },
    { 
      field: 'status', 
      headerName: 'Status',
      sortable: true,
      filter: 'agSetColumnFilter',
      cellRenderer: params => {
        const status = params.value;
        const color = status === 'Active' ? 'green' : 'red';
        return `<span style="color: ${color}; font-weight: bold;">${status}</span>`;
      }
    }
  ]);

  const defaultColDef = {
    flex: 1,
    sortable: true,
    filter: true,
    resizable: true
  };

  const onGridReady = useCallback((params) => {
    console.log('Grid ready with', params.api.getDisplayedRowCount(), 'rows');
    // Auto-size columns to fit content
    params.api.sizeColumnsToFit();
  }, []);

  const onSelectionChanged = useCallback(() => {
    const selectedRows = gridRef.current.api.getSelectedRows();
    console.log('Selected rows:', selectedRows);
  }, []);

  const onExportClick = useCallback(() => {
    gridRef.current.api.exportDataAsCsv({
      fileName: 'employees.csv',
      onlySelected: false
    });
  }, []);

  const onFilterChanged = useCallback(() => {
    const rowCount = gridRef.current.api.getDisplayedRowCount();
    console.log('Filtered rows:', rowCount);
  }, []);

  return (
    <div style={{ padding: '20px' }}>
      <div style={{ marginBottom: '20px' }}>
        <h2>Employee Management Table</h2>
        <button 
          onClick={onExportClick}
          style={{
            padding: '10px 20px',
            backgroundColor: '#007bff',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer'
          }}
        >
          Export to CSV
        </button>
      </div>
      <div 
        className="ag-theme-quartz" 
        style={{ height: 600, width: '100%' }}
      >
        <AgGridReact
          ref={gridRef}
          rowData={rowData}
          columnDefs={columnDefs}
          defaultColDef={defaultColDef}
          rowSelection="multiple"
          pagination={true}
          paginationPageSize={10}
          animateRows={true}
          onGridReady={onGridReady}
          onSelectionChanged={onSelectionChanged}
          onFilterChanged={onFilterChanged}
        />
      </div>
    </div>
  );
}

export default EmployeeTable;
Enter fullscreen mode Exit fullscreen mode

This example demonstrates:

  • Multi-row selection with checkboxes
  • Editable cells with validation
  • Custom cell renderers and formatters
  • Conditional cell styling
  • Export functionality
  • Advanced filtering (text, number, date, set filters)
  • Event handling for grid interactions

Common Issues / Troubleshooting

  1. Grid not displaying: Ensure you've imported both CSS files (ag-grid.css and a theme CSS). The container div must have an explicit height.

  2. Styling issues: Make sure you're applying the theme class (ag-theme-quartz) to the container div, not just importing the CSS.

  3. Performance with large datasets: For datasets with 1000+ rows, consider enabling virtualization (enabled by default) or using the server-side row model for better performance.

  4. TypeScript errors: If using TypeScript, install @types/react and ensure your column definitions match the ColDef type from ag-grid-community.

  5. Cell editing not working: Verify that editable: true is set in column definitions and that you're handling onCellValueChanged events if needed.

Next Steps

Now that you understand the basics of AG Grid:

  • Explore advanced features like custom cell renderers, cell editors, and value getters
  • Learn about server-side row model for handling large datasets
  • Implement custom filters and sorting logic
  • Add row grouping and aggregation features
  • Explore the enterprise features (if using AG Grid Enterprise)
  • Check the official documentation: https://www.ag-grid.com/react-data-grid/
  • Look for part 3 of this series for more advanced topics

Summary

You've learned how to set up AG Grid in React and create feature-rich data tables with sorting, filtering, pagination, and editing capabilities. AG Grid provides a robust foundation for building enterprise-grade data management interfaces with excellent performance and extensive customization options.

Top comments (0)