Building Interactive Dashboards with AG-Grid and Charts in React
Creating dynamic and insightful data dashboards is crucial for data-driven decision-making. When working with large datasets in React applications, combining the robust data grid capabilities of AG-Grid with powerful charting libraries offers an unparalleled solution. This post, part of our AG-Grid React Series, will guide you through building an interactive dashboard where data presented in AG-Grid can directly influence and update visualizations from a charting library.
The synergy between a feature-rich grid and intuitive charts empowers users to explore data more deeply, identifying trends, anomalies, and opportunities at a glance. Let's dive in!
Prerequisites
Before we begin, ensure you have a basic understanding of:
- React.js: Fundamental concepts like components, props, state, and hooks.
- AG-Grid: Basic setup, `rowData`, `columnDefs`, and how to interact with the grid API.
- A Charting Library: While the principles apply to any, we'll use Recharts for our examples due to its React-native approach and popularity.
Setting Up Your Project
If you haven't already, start by creating a new React project and installing the necessary dependencies:
npx create-react-app ag-grid-dashboard-app
cd ag-grid-dashboard-app
npm install ag-grid-community ag-grid-react recharts
# or
yarn add ag-grid-community ag-grid-react recharts
The Core Components of Our Dashboard
A typical interactive dashboard using AG-Grid and charts will involve:
- The AG-Grid Component: Displays raw data, allows filtering, sorting, and other data manipulations.
- The Chart Component: Visualizes summary or aggregated data derived from the AG-Grid.
- A Dashboard Container: Orchestrates the data flow between the grid and the charts, managing layout and state.
1. Integrating AG-Grid
Let's start by setting up a basic AG-Grid component. We'll use some dummy sales data for demonstration purposes. In your `src/App.js` (or a dedicated `Dashboard.js` component), define your `rowData` and `columnDefs`.
import React, { useState, useEffect, useRef, useCallback } from 'react';
import { AgGridReact } from 'ag-grid-react';
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
const initialRowData = [
{ product: 'Laptop', category: 'Electronics', price: 1200, quantity: 50, revenue: 60000 },
{ product: 'Mouse', category: 'Electronics', price: 25, quantity: 200, revenue: 5000 },
{ product: 'Keyboard', category: 'Electronics', price: 75, quantity: 120, revenue: 9000 },
{ product: 'Desk Chair', category: 'Furniture', price: 300, quantity: 30, revenue: 9000 },
{ product: 'Monitor', category: 'Electronics', price: 250, quantity: 80, revenue: 20000 },
{ product: 'Coffee Table', category: 'Furniture', price: 150, quantity: 40, revenue: 6000 },
{ product: 'Webcam', category: 'Accessories', price: 60, quantity: 100, revenue: 6000 },
{ product: 'Headphones', category: 'Accessories', price: 100, quantity: 150, revenue: 15000 },
{ product: 'Table Lamp', category: 'Home Decor', price: 40, quantity: 70, revenue: 2800 },
{ product: 'Bookshelf', category: 'Furniture', price: 180, quantity: 25, revenue: 4500 },
];
const columnDefs = [
{ field: 'product', filter: true, sortable: true },
{ field: 'category', filter: true, sortable: true },
{ field: 'price', sortable: true, valueFormatter: p => '$' + p.value.toLocaleString() },
{ field: 'quantity', sortable: true },
{ field: 'revenue', sortable: true, valueFormatter: p => '$' + p.value.toLocaleString() },
];
function Dashboard() {
const gridRef = useRef();
const [rowData] = useState(initialRowData);
const [chartData, setChartData] = useState([]);
// ... chart update logic and JSX will go here
}
2. Integrating Charts with Recharts
Next, let's create a simple chart component that will visualize our data. For our example, we'll create a bar chart showing the total quantity sold per category. This chart will eventually reflect the data currently visible in the AG-Grid.
// Inside Dashboard component, after state declarations
const processChartData = useCallback((data) => {
const categoryMap = {};
data.forEach(item => {
if (categoryMap[item.category]) {
categoryMap[item.category] += item.quantity;
} else {
categoryMap[item.category] = item.quantity;
}
});
return Object.keys(categoryMap).map(category => ({
category: category,
'Total Quantity': categoryMap[category]
}));
}, []);
const onGridReady = useCallback((params) => {
gridRef.current.api = params.api;
// Initialize chart with all data on grid ready
setChartData(processChartData(initialRowData));
}, [processChartData]);
const updateChartOnGridChange = useCallback(() => {
if (gridRef.current.api) {
const currentFilteredData = [];
gridRef.current.api.forEachNodeAfterFilterAndSort(node => {
currentFilteredData.push(node.data);
});
setChartData(processChartData(currentFilteredData));
}
}, [processChartData]);
// Use useEffect to trigger chart update when rowData changes (or on initial load)
useEffect(() => {
// This useEffect is mainly for initial load, subsequent updates are by onFilterChanged/onSortChanged
updateChartOnGridChange();
}, [updateChartOnGridChange]); // Dependency array includes updateChartOnGridChange
return (
<div style={{ display: 'flex', flexDirection: 'column', height: 'calc(100vh - 20px)', padding: '10px' }}>
<h2>Sales Dashboard</h2>
<div style={{ flex: 1, marginBottom: '20px' }}>
<h3>Quantity by Category (Filtered Data)</h3>
<ResponsiveContainer width="100%" height="100%">
<BarChart
data={chartData}
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="category" />
<YAxis />
<Tooltip />
<Legend />
<Bar dataKey="Total Quantity" fill="#8884d8" />
</BarChart>
</ResponsiveContainer>
</div>
<div style={{ flex: 1, width: '100%' }} className="ag-theme-alpine">
<h3>Sales Data Grid</h3>
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={{
flex: 1,
minWidth: 100,
editable: true,
resizable: true,
}}
pagination={true}
paginationPageSize={10}
onGridReady={onGridReady}
onFilterChanged={updateChartOnGridChange} // Update chart when filters change
onSortChanged={updateChartOnGridChange} // Update chart when sorting changes
>
</AgGridReact>
</div>
</div>
);
}
export default Dashboard;
Making it Dynamic: Connecting AG-Grid and Charts
The real power of a dashboard comes from its interactivity. When a user filters, sorts, or groups data in AG-Grid, the charts should ideally reflect these changes. Here's how we achieve that:
- Get Grid API Reference: We use `useRef` to get a reference to the AG-Grid API when the grid is ready (`onGridReady`). This API allows us to programmatically interact with the grid.
- Derive Chart Data from Grid State: We need a function (`processChartData` in our example) that takes a dataset (like our `initialRowData` or the filtered/sorted data from the grid) and transforms it into the format expected by our charting library.
- Listen for Grid Events: AG-Grid provides events like `onFilterChanged`, `onSortChanged`, `onSelectionChanged`, etc. We attach our chart update logic to these events.
- Update Chart State: Inside the event handlers, we use the AG-Grid API (`gridRef.current.api.forEachNodeAfterFilterAndSort` is very useful here) to retrieve the currently displayed (filtered and sorted) data. This data is then passed to our `processChartData` function, and the resulting chart-specific data updates the React state (`chartData`), causing the Recharts component to re-render.
In the comprehensive code example above, notice how `onFilterChanged` and `onSortChanged` on the `AgGridReact` component trigger the `updateChartOnGridChange` function. This function accesses the grid's current state (filtered/sorted rows) and then updates the `chartData` state, which in turn causes the `BarChart` to render the new visualization.
Dashboard Layout Considerations
For arranging your grid and chart components, consider using modern CSS layout techniques:
- CSS Flexbox: Great for one-dimensional layouts (rows or columns). Our example uses `display: 'flex'` with `flexDirection: 'column'` to stack the chart and grid.
- CSS Grid: Ideal for two-dimensional layouts, offering more control over rows and columns for complex dashboards.
Ensure your chart components use `ResponsiveContainer` (from Recharts) or similar solutions from other libraries to adapt to available space.
Advanced Considerations
- Performance Optimization: For very large datasets, be mindful of how frequently you process data for charts. Consider debouncing chart updates or using web workers for heavy aggregation. AG-Grid's built-in row models (e.g., Server-Side Row Model) can offload data processing to the backend.
- More Complex Charts: Incorporate multiple charts (pie charts, line charts, scatter plots) visualizing different aspects or aggregated views of your AG-Grid data.
- Cross-Component Interaction: Implement more sophisticated interactions, such as clicking a bar in a chart to filter the AG-Grid, or selecting rows in AG-Grid to highlight points on a chart. This often involves using the AG-Grid API to set filters or selection programmatically.
- Theming: Consistent styling across AG-Grid and your charts enhances user experience. Both AG-Grid and most charting libraries offer extensive theming options.
Conclusion
Building interactive dashboards with AG-Grid and a charting library like Recharts in React provides a powerful way to present and analyze data. By intelligently connecting the data flow between your grid and your visualizations, you empower users with dynamic tools to extract meaningful insights. Start experimenting with different data transformations and chart types to unlock the full potential of your data applications!