<DatagridAG>

This Enterprise Edition component is an alternative datagrid component with advanced features, based on ag-grid.

Note: This component is still in alpha stage. Major changes may still be introduced in the future.

<DatagridAG> supports all the <Datagrid> features, plus some exclusive ag-grid features:

  • In place editing of cells or rows
  • DOM virtualization
  • Columns resizing and reordering
  • Column & Row pinning
  • Cell expressions
  • Row animation
  • Advanced filtering
  • Multi-column sorting
  • Keyboard navigation
  • Row dragging
  • Themes
  • Flashing Cells
  • Tooltips

The <DatagridAG> component provides a smooth integration of ag-grid with react-admin, offering the following features out of the box:

  • Data read from ListContext
  • Loading state
  • Compatibility with React Admin fields
  • Persistence of the columns order and size
  • Bulk Actions

Additionally, <DatagridAG> is compatible with the Enterprise version of ag-grid, which offers even more features:

  • Row Grouping
  • Aggregation
  • Tree Data
  • Pivoting
  • More advanced filtering
  • Master Detail views
  • Range Selection
  • Excel Export
  • Context Menu
  • And more…

Usage

First, install the @react-admin/ra-datagrid-ag package:

npm install --save @react-admin/ra-datagrid-ag
# or
yarn add @react-admin/ra-datagrid-ag

Tip: ra-datagrid-ag is hosted in a private npm registry. You need to subscribe to one of the Enterprise Edition plans to access this package.

Then, use <DatagridAG> as a child of a react-admin <List>, <ReferenceManyField>, or any other component that creates a ListContext.

import React from 'react';
import { List } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';
import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';

export const PostList = () => {
    const columnDefs = [
        { field: 'title' },
        { field: 'published_at' },
        { field: 'body' },
    ];
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG columnDefs={columnDefs} />
        </List>
    );
};

DatagridAG PostList

Here are the important things to note:

  • You need to import the ag-grid stylesheets (ag-grid.css and ag-theme-alpine.css).
  • To benefit from ag-grid’s filtering and sorting features (as well as some Enterprise features like grouping), you need to load the entire list of records client-side. To do so, you must set <List perPage> to a high number (e.g. 10,000).
  • As the pagination is handled by ag-grid, you can disable react-admin’s pagination with <List pagination={false}>.
  • The columns are defined using the columnDefs prop. See the dedicated doc section for more information.

<DatagridAG> doesn’t currently support the server-side row model, so you have to load all data client-side. The client-side performance isn’t affected by a large number of records, as ag-grid uses DOM virtualization. <DatagridAG> has been tested with 10,000 records without any performance issue.

Props

Prop Required Type Default Description
columnDefs Required Array n/a The columns definitions
bulkActionButtons Optional Element <BulkDelete Button> The component used to render the bulk action buttons
cellRenderer Optional String, Function or Element   Allows to use a custom component to render the cell content
defaultColDef Optional Object   The default column definition (applied to all columns)
mutationOptions Optional Object   The mutation options
preferenceKey Optional String or false ${resource}.ag-grid.params The key used to persist gridState in the Store. false disables persistence.
sx Optional Object   The sx prop passed down to the wrapping <div> element
theme Optional String 'ag-theme-alpine' The name of the ag-grid theme
pagination Optional Boolean true Enable or disable pagination

<DatagridAG> also accepts the same props as <AgGridReact> with the exception of rowData, since the data is fetched from the List context.

Defaults

Under the hood, <DatagridAG> is a wrapper around <AgGridReact>. However, it sets some important default values:

  • pagination is set to true
  • paginationAutoPageSize is set to true
  • animateRows is set to true
  • rowSelection is set to 'multiple'
  • suppressRowClickSelection is set to true
  • readOnlyEdit is set to true
  • getRowId is set to use the record id field

It also includes a defaultColDef object with the following properties:

{
    resizable: true,
    filter: true,
    sortable: true,
    editable: true,
    headerCheckboxSelectionFilteredOnly: true,
    headerCheckboxSelectionCurrentPageOnly: true,
}

You may override any of these defaults by passing the corresponding props to <DatagridAG> (defaultColDef will be merged with the defaults).

columnDefs

The columnDefs prop is the most important prop of <DatagridAG>. It defines the columns of the grid, and their properties. It is an array of objects, each object representing a column.

Here is an example with a complete column definitions object:

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import React from 'react';
import { List } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';

const truncate = (str: string, n: number) => {
    return str.length > n ? str.slice(0, n - 1) + '...' : str;
};

export const PostList = () => {
    const columnDefs = [
        {
            field: 'id',
            editable: false,
            headerCheckboxSelection: true,
            checkboxSelection: true,
            minWidth: 48,
            maxWidth: 48,
        },
        { field: 'title' },
        {
            field: 'published_at',
            headerName: 'Publication Date',
        },
        {
            field: 'body',
            cellRenderer: ({ value }) => truncate(value, 20),
        },
    ];
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG columnDefs={columnDefs} />
        </List>
    );
};

DatagridAG custom columnDefs

Have a look at the ag-grid documentation for the exhaustive list of column properties.

defaultColDef

The defaultColDef prop allows you to define default properties for all columns. It is an object with the same properties as columnDefs objects.

In the example below, we enable flex mode on the columns, and set each column to take 1/3 of the available space:

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import React from 'react';
import { List } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';

export const PostList = () => {
    const columnDefs = [
        { field: 'title' },
        { field: 'published_at' },
        { field: 'body' },
    ];
    const defaultColDef = {
        flex: 1,
    };
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG columnDefs={columnDefs} defaultColDef={defaultColDef} />
        </List>
    );
};

DatagridAG defaultColDef

cellRenderer

In a column definition, you can use the cellRenderer field to specify a custom cell renderer. In addition to ag-grid’s cell rendering abilities, <DatagridAG> supports react-admin fields in cellRenderer. This is particularly useful to render a <ReferenceField> for instance.

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import React from 'react';
import { EmailField, List, ReferenceField, TextField } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';

export const CommentList = () => {
    const columnDefs = [
        {
            field: 'id',
            editable: false,
        },
        { field: 'author.name' },
        {
            field: 'author.email',
            cellRenderer: <EmailField source="author.email" />,
        },
        {
            field: 'post_id',
            headerName: 'Post',
            cellRenderer: (
                <ReferenceField source="post_id" reference="posts">
                    <TextField source="title" />
                </ReferenceField>
            ),
        },
        { field: 'created_at' },
        { field: 'body' },
    ];
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG columnDefs={columnDefs} />
        </List>
    );
};

DatagridAG RA Fields

Note: You still need to pass the source prop to the field.

bulkActionButtons

You can use the bulkActionButtons prop to customize the bulk action buttons, displayed when at least one row is selected.

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import React from 'react';
import { List, BulkExportButton, BulkDeleteButton } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';

const PostBulkActionButtons = () => (
    <>
        <BulkExportButton />
        <BulkDeleteButton />
    </>
);

export const PostList = () => {
    const columnDefs = [
        {
            headerCheckboxSelection: true,
            checkboxSelection: true,
            editable: false,
            minWidth: 48,
            maxWidth: 48,
        },
        { field: 'title' },
        { field: 'published_at' },
        { field: 'body' },
    ];
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG
                columnDefs={columnDefs}
                bulkActionButtons={<PostBulkActionButtons />}
            />
        </List>
    );
};

mutationOptions

You can use the mutationOptions prop to provide options to the dataProvider.update() call triggered when a cell or a row is edited.

In particular, this allows to choose the mutationMode, and/or to pass a meta object to the dataProvider.

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import React from 'react';
import { List } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';

export const PostList = () => {
    const columnDefs = [
        { field: 'title' },
        { field: 'published_at' },
        { field: 'body' },
    ];
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG
                columnDefs={columnDefs}
                mutationOptions={{
                    meta: { foo: 'bar' },
                    mutationMode: 'optimistic',
                }}
            />
        </List>
    );
};

This also allows to display a notification after the mutation succeeds.

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import React from 'react';
import { List, useNotify } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';

export const PostList = () => {
    const columnDefs = [
        { field: 'title' },
        { field: 'published_at' },
        { field: 'body' },
    ];
    const notify = useNotify();
    const onSuccess = React.useCallback(() => {
        notify('ra.notification.updated', {
            type: 'info',
            messageArgs: {
                smart_count: 1,
            },
            undoable: true,
        });
    }, [notify]);
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG
                columnDefs={columnDefs}
                mutationOptions={{
                    mutationMode: 'undoable',
                    onSuccess,
                }}
            />
        </List>
    );
};

theme

You can use a different theme for the grid by passing a theme prop. You can for instance use one of the themes provided by ag-grid, like ag-theme-balham or ag-theme-alpine-dark:

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import React from 'react';
import { List } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';

export const PostList = () => {
    const columnDefs = [
        { field: 'title' },
        { field: 'published_at' },
        { field: 'body' },
    ];
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG columnDefs={columnDefs} theme="ag-theme-alpine-dark" />
        </List>
    );
};

DatagridAG Dark

Tip: Remember to import the corresponding stylesheet (e.g. ag-theme-balham[.min].css for ag-theme-balham).

pagination

Enable or disable the pagination controls at the bottom of the list. Defaults to true.

Disabling the pagination switches the grid to infinite pagination mode: Users reveal new rows simply by scrolling down. This is fast even for large lists thanks to ag-grid’s DOM virtualization.

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import React from 'react';
import { List } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';

const CarList = () => {
    const columnDefs = [
        { field: 'make' },
        { field: 'model' },
        { field: 'price' },
    ];
    const defaultColDef = {
        flex: 1,
    };
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG
                columnDefs={columnDefs}
                defaultColDef={defaultColDef}
                pagination={false}
            />
        </List>
    );
};

preferenceKey

<DatagridAG> will store the gridState in the Store, under the key ${resource}.ag-grid.params.grid. This gridState persisted in the store is applied once when the grid is created, it means that users will find the grid as they left it previously.

If you wish to change the key used to store the columns order and size, you can pass a preferenceKey prop to <DatagridAG>.

<List perPage={10000} pagination={false}>
    <DatagridAG columnDefs={columnDefs} preferenceKey="my-post-list" />
</List>

If, instead, you want to disable the persistence of the columns order and size, you can pass false to the preferenceKey prop:

<List perPage={10000} pagination={false}>
    <DatagridAG columnDefs={columnDefs} preferenceKey={false} />
</List>

Tip: If you update the columnDefs prop, make sure to clear or invalidate the React-admin store in order to view your changes.

sx

You can also use the sx prop to customize the grid’s style:

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import React from 'react';
import { List } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';

export const PostList = () => {
    const columnDefs = [
        { field: 'title' },
        { field: 'published_at' },
        { field: 'body' },
    ];
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG
                columnDefs={columnDefs}
                sx={{ '& .ag-header-cell-comp-wrapper': { color: 'red' } }}
            />
        </List>
    );
};

DatagridAG sx

It can also be helpful to change the default grid’s height (calc(100vh - 96px - ${theme.spacing(1)})):

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import React from 'react';
import { List } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';

export const PostList = () => {
    const columnDefs = [
        /* ... */
    ];
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG
                columnDefs={columnDefs}
                sx={{ height: 'calc(100vh - 250px)' }}
            />
        </List>
    );
};

DatagridAG sx height

Accessing The Grid And Column APIs

You can access the grid’s api by passing a ref to <DatagridAG>.

In this example, we use the api to automatically resize all columns to fit their content on first render:

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import React from 'react';
import { AgGridReact } from 'ag-grid-react';
import { List } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';

export const PostList = () => {
    const columnDefs = [
        { field: 'title' },
        { field: 'published_at' },
        { field: 'body' },
    ];
    const gridRef = React.useRef<AgGridReact>(null);
    const onFirstDataRendered = React.useCallback(() => {
        gridRef.current.api.autoSizeAllColumns();
    }, []);
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG
                columnDefs={columnDefs}
                ref={gridRef}
                onFirstDataRendered={onFirstDataRendered}
            />
        </List>
    );
};

Check out the Grid API and Column API documentations to learn more.

Changing The Default Column Width

By default, ag-grid will render each column with a fixed size.

You can choose to enable flex mode by setting the flex prop either on the columnDefs or on the defaultColDef:

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import React from 'react';
import { List } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';

export const PostList = () => {
    const columnDefs = [
        { field: 'title' },
        { field: 'published_at', flex: 1 },
        { field: 'body' },
    ];
    const defaultColDef = {
        flex: 2,
    };
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG columnDefs={columnDefs} defaultColDef={defaultColDef} />
        </List>
    );
};

DatagridAG flex

Alternatively, you can use the grid’s api to call autoSizeAllColumns to automatically resize all columns to fit their content:

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import React from 'react';
import { AgGridReact } from 'ag-grid-react';
import { List } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';

export const PostList = () => {
    const columnDefs = [
        { field: 'title' },
        { field: 'published_at' },
        { field: 'body' },
    ];
    const gridRef = React.useRef<AgGridReact>(null);
    const onFirstDataRendered = React.useCallback(() => {
        gridRef.current.api.autoSizeAllColumns();
    }, []);
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG
                columnDefs={columnDefs}
                ref={gridRef}
                onFirstDataRendered={onFirstDataRendered}
            />
        </List>
    );
};

DatagridAG auto size

Check out the Column Sizing documentation for more information and more alternatives.

Selecting Rows And Enabling Bulk Actions

Just like <Datagrid>, <DatagridAG> supports row selection and bulk actions.

Add a column with the following definition to enable row selection:

{
    headerCheckboxSelection: true,
    checkboxSelection: true,
    editable: false,
    minWidth: 48,
    maxWidth: 48,
},

Below is an example with the PostList component:

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import React from 'react';
import { List } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';

export const PostList = () => {
    const columnDefs = [
        {
            headerCheckboxSelection: true,
            checkboxSelection: true,
            editable: false,
            minWidth: 48,
            maxWidth: 48,
        },
        { field: 'title' },
        { field: 'published_at' },
        { field: 'body' },
    ];
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG columnDefs={columnDefs} />
        </List>
    );
};

DatagridAG selected rows

Just like with <Datagrid>, you can customize the bulk actions by passing a bulkActionButtons prop to <DatagridAG>.

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import React from 'react';
import { List, BulkExportButton, BulkDeleteButton } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';

const PostBulkActionButtons = () => (
    <>
        <BulkExportButton />
        <BulkDeleteButton />
    </>
);

export const PostList = () => {
    const columnDefs = [
        {
            headerCheckboxSelection: true,
            checkboxSelection: true,
            editable: false,
            minWidth: 48,
            maxWidth: 48,
        },
        { field: 'title' },
        { field: 'published_at' },
        { field: 'body' },
    ];
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG
                columnDefs={columnDefs}
                bulkActionButtons={<PostBulkActionButtons />}
            />
        </List>
    );
};

Enabling Infinite Pagination

By default, <DatagridAG> renders pagination controls at the bottom of the list. You can disable these controls to switch to an infinite pagination mode, where the grid shows the next rows on scroll. Thanks to ag-grid’s DOM virtualization, this mode causes no performance problem.

To enable infinite pagination, set the pagination prop to false.

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import React from 'react';
import { List } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';

const CarList = () => {
    const columnDefs = [
        { field: 'make' },
        { field: 'model' },
        { field: 'price' },
    ];
    const defaultColDef = {
        flex: 1,
    };
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG
                columnDefs={columnDefs}
                defaultColDef={defaultColDef}
                pagination={false}
            />
        </List>
    );
};

If you have subscribed to the Enterprise version of ag-grid, you can also add a Status Bar to show the total number of rows.

DatagridAG with status bar

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import React, { useMemo } from 'react';
import { List } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';
import 'ag-grid-enterprise';

const CarList = () => {
    const statusBar = useMemo(() => {
        return {
            statusPanels: [
                {
                    statusPanel: 'agTotalAndFilteredRowCountComponent',
                    align: 'left',
                },
            ],
        };
    }, []);
    const columnDefs = [{ field: 'make' }, { field: 'model' }, { field: 'price' }];
    const defaultColDef = { flex: 1 };
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG
                columnDefs={columnDefs}
                defaultColDef={defaultColDef}
                pagination={false}
                statusBar={statusBar}
            />
        </List>
    );
};

Enabling Full Row Edition

By default, editing is enabled on cells, which means you can edit a cell by double-clicking on it, and it will trigger a call to the dataProvider’s update function.

DatagridAG edit cell

However, if you’d like to update the full row at once instead, you can enable full row editing by passing editType="fullRow" to <DatagridAG>:

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import React from 'react';
import { List } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';

export const PostList = () => {
    const columnDefs = [
        /* ... */
    ];
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG columnDefs={columnDefs} editType="fullRow" />
        </List>
    );
};

DatagridAG edit row

Disabling Cell Edition

Set editable: false in the definition of a column to disable the ability to edit its cells.

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import React from 'react';
import { List } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';

export const PostList = () => {
    const columnDefs = [
        { field: 'title' },
        { field: 'published_at', editable: false },
        { field: 'body' },
    ];
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG columnDefs={columnDefs} />
        </List>
    );
};

Alternatively, you can disable the ability to edit all cells by passing editable: false to the defaultColDef:

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import React from 'react';
import { List } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';

export const PostList = () => {
    const columnDefs = [
        { field: 'title' },
        { field: 'published_at' },
        { field: 'body' },
    ];
    const defaultColDef = {
        editable: false,
    };
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG
                columnDefs={columnDefs}
                defaultColDef={defaultColDef}
            />
        </List>
    );
};

Using AG Grid Enterprise

<DatagridAG> is also compatible with the Enterprise version of ag-grid.

You can follow the instructions in the Getting Started with AG Grid Enterprise section of the Getting Started documentation to enable the Enterprise features.

Below is a short example of what you can achieve.

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-enterprise';
import React from 'react';
import { List } from 'react-admin';
import { DatagridAG } from '@react-admin/ra-datagrid-ag';

const OlympicWinnersList = () => {
    const columnDefs = [
        {
            headerCheckboxSelection: true,
            checkboxSelection: true,
            editable: false,
            minWidth: 48,
            maxWidth: 48,
        },
        { field: 'athlete' },
        { field: 'age' },
        { field: 'country' },
        { field: 'year' },
        { field: 'date' },
        { field: 'sport' },
        { field: 'gold' },
        { field: 'silver' },
        { field: 'bronze' },
        { field: 'total' },
    ];
    const gridRef = React.useRef<AgGridReact>(null);
    const onFirstDataRendered = React.useCallback(() => {
        gridRef.current.api.autoSizeAllColumns();
    }, []);
    const defaultColDef = {
        enableRowGroup: true,
    };
    return (
        <List perPage={10000} pagination={false}>
            <DatagridAG
                columnDefs={columnDefs}
                defaultColDef={defaultColDef}
                ref={gridRef}
                onFirstDataRendered={onFirstDataRendered}
                rowGroupPanelShow="always"
                groupSelectsChildren
            />
        </List>
    );
};