About ag-grid website npm i ag-grid-community free package npm i ag-grid-react free package npm i ag-grid-enterprise paid features useData In several examples we call for data from the api To avoid excessive api calls we can use caching via useQuery package const { useQuery } = require('react-query') export function useData() { return useQuery({ queryKey: ['ag-grid-data'], queryFn: () => fetch('https://www.ag-grid.com/example-assets/olympic-winners.json') .then((resp) => resp.json()) .then(data => data) }) } Basics import { useEffect, useRef, useState } from 'react' import { AgGridReact } from 'ag-grid-react' import 'ag-grid-community/styles/ag-grid.css' import 'ag-grid-community/styles/ag-theme-alpine.css' const CustomRenderer = ({ value }) => ( <> <img src='https://d1yk6z6emsz7qy.cloudfront.net/static/images/loading.gif' style={{ width: '40px', position: 'absolute', top: '0px', left: '0px' }} alt='alt' /> <span style={{ marginLeft: '20px' }}> {value + ' EUR'}</span> </> ) export const Example1 = () => { const gridRef = useRef() useEffect(() => { console.log(gridRef.current) }, []) const [columnDefs] = useState([ { field: 'make', editable: false, unSortIcon: true }, { field: 'model', minWidth: 200 }, { field: 'price', cellRenderer: CustomRenderer } ]) const getTableRows = () => { const array = [ { make: 'Toyota', model: 'Celica', price: 35000 }, { make: 'Ford', model: 'Mondeo', price: 32000 }, { make: 'Porsche', model: 'Boxster', price: 72000 }, { make: 'Volvo', model: 'CX90', price: 45000 }, { make: 'BMW', model: '3', price: 37000 }, { make: 'Nissan', model: 'X-trail', price: 66000 } ] for (let i = 0; i < 999999; i++) { array.push({ make: 'make ' + i, model: 'model ' + i, price: i }) } return array } const [rowData] = useState(getTableRows()) return ( <div className='ag-theme-alpine' style={{ height: '400px', width: '100%', marginBottom: '30px' }}> <button onClick={() => { gridRef.current.api.selectAll() }} > Select all </button> <AgGridReact ref={gridRef} columnDefs={columnDefs} rowData={rowData} defaultColDef={{ editable: true, sortable: true, flex: 1, minWidth: 100, filter: true, resizable: true }} // rowSelection='multiple' animateRows rowHeight={70} // pagination enableRangeSelection suppressContextMenu statusBar={{ statusPanels: [ { statusPanel: 'agTotalAndFilteredRowCountComponent', align: 'left' }, { statusPanel: 'agTotalRowCountComponent', align: 'center' }, { statusPanel: 'agFilteredRowCountComponent' }, { statusPanel: 'agSelectedRowCountComponent' }, { statusPanel: 'agAggregationComponent' } ] }} onRowClicked={(e) => console.log('row clicked', e)} onCellClicked={(e) => console.log('cell clicked', e)} onColumnResized={(e) => console.log('column resized', e)} onGridReady={(e) => { console.log({ 'gridRef.current': gridRef.current }) console.log({ e }) }} /> </div> ) } Consume data from complex object import { useState } from 'react' import { AgGridReact } from 'ag-grid-react' import 'ag-grid-community/styles/ag-grid.css' import 'ag-grid-community/styles/ag-theme-alpine.css' const rows = [ { name: 'Michael Phelps', person: { age: 23, country: 'United States' }, medals: { gold: 8, silver: 0, bronze: 0 } }, { name: 'Michael Phelps', person: { age: 19, country: 'United States' }, medals: { gold: 6, silver: 0, bronze: 2 } }, { name: 'Michael Phelps', person: { age: 27, country: 'United States' }, medals: { gold: 4, silver: 2, bronze: 0 } }, { name: 'Natalie Coughlin', person: { age: 25, country: 'United States' }, medals: { gold: 1, silver: 2, bronze: 3 } }, { name: 'Ryan Lochte', person: { age: 24, country: 'United States' }, medals: { gold: 2, silver: 0, bronze: 2 } }, { name: 'Inge de Bruijn', person: { age: 30, country: 'Netherlands' }, medals: { gold: 1, silver: 1, bronze: 2 } } ] const gridStyle = { height: '300px', width: '100%' } export const Example2 = () => { const [rowData, setRowData] = useState(rows) const [columnDefs, setColumnDefs] = useState([ { field: 'name' }, { field: 'medals.gold', headerName: 'Gold' }, { field: 'person.age' } ]) return ( <div style={gridStyle} className='ag-theme-alpine'> <AgGridReact rowData={rowData} columnDefs={columnDefs} /> </div> ) } floatingFilter import { AgGridReact } from 'ag-grid-react' import 'ag-grid-community/styles/ag-grid.css' import 'ag-grid-community/styles/ag-theme-alpine.css' import { useData } from './useData' const columnDefs = [ { field: 'athlete' }, { field: 'sport' }, { field: 'age', type: 'numberColumn' }, { field: 'year', type: 'numberColumn' }, { field: 'date', type: ['dateColumn', 'nonEditableColumn'], width: 220 }, { headerName: 'Medals', groupId: 'medalsGroup', children: [ { headerName: 'Gold', field: 'gold', type: 'medalColumn' }, { headerName: 'Silver', field: 'silver', type: 'medalColumn' }, { headerName: 'Bronze', field: 'bronze', type: 'medalColumn' }, { headerName: 'Total', field: 'total', type: 'medalColumn', columnGroupShow: 'closed' } ] } ] const defaultColDef = { width: 150, editable: true, filter: 'agTextColumnFilter', // make every column use 'text' filter by default floatingFilter: true, resizable: true } const defaultColGroupDef = { marryChildren: true } const columnTypes = { numberColumn: { width: 130, filter: 'agNumberColumnFilter' }, medalColumn: { width: 100, columnGroupShow: 'open', filter: false }, nonEditableColumn: { editable: false }, dateColumn: { // specify we want to use the date filter filter: 'agDateColumnFilter', // add extra parameters for the date filter filterParams: { // provide comparator function comparator: (filterLocalDateAtMidnight, cellValue) => { // In the example application, dates are stored as dd/mm/yyyy const dateParts = cellValue.split('/') const day = Number(dateParts[0]) const month = Number(dateParts[1]) - 1 const year = Number(dateParts[2]) const cellDate = new Date(year, month, day) if (cellDate < filterLocalDateAtMidnight) return -1 if (cellDate > filterLocalDateAtMidnight) return 1 return 0 } } } } const gridStyle = { height: '300px', width: '100%' } export const Example3 = () => { const { data: rowData } = useData() return ( <div style={gridStyle} className='ag-theme-alpine'> <AgGridReact rowData={rowData} columnDefs={columnDefs} defaultColDef={defaultColDef} defaultColGroupDef={defaultColGroupDef} columnTypes={columnTypes} // onGridReady={onGridReady} /> </div> ) } Add / remove columns programmatically /** @jsxImportSource @emotion/react */ import { css } from '@emotion/react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { AgGridReact } from 'ag-grid-react' import 'ag-grid-community/styles/ag-grid.css' import 'ag-grid-community/styles/ag-theme-alpine.css' import { useQuery } from 'react-query' import { useData } from './useData' const columnDefsMedalsIncluded = [ { field: 'athlete' }, { field: 'gold' }, { field: 'silver' }, { field: 'bronze' }, { field: 'total' }, { field: 'age' }, { field: 'country' }, { field: 'sport' }, { field: 'year' }, { field: 'date' } ] const colDefsMedalsExcluded = [ { field: 'athlete' }, { field: 'age' }, { field: 'country' }, { field: 'sport' }, { field: 'year' }, { field: 'date' } ] export const Example4 = () => { const gridRef = useRef() const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []) const [rowData, setRowData] = useState() const { data } = useData() useEffect(() => { setRowData(data) }, [data]) const [columnDefs, setColumnDefs] = useState(columnDefsMedalsIncluded) const onBtExcludeMedalColumns = useCallback(() => { gridRef.current.api.setColumnDefs(colDefsMedalsExcluded) }, []) const onBtIncludeMedalColumns = useCallback(() => { gridRef.current.api.setColumnDefs(columnDefsMedalsIncluded) }, []) return ( <div css={css` .test-container { height: 400px; } `} > <div className='test-container'> <div> <button onClick={onBtExcludeMedalColumns}> Exclude Medal Columns </button> <button onClick={onBtIncludeMedalColumns}> Include Medal Columns </button> </div> <div style={gridStyle} className='ag-theme-alpine'> <AgGridReact ref={gridRef} rowData={rowData} columnDefs={columnDefs} defaultColDef={{ initialWidth: 100, sortable: true, resizable: true }} // onGridReady={onGridReady} /> </div> </div> </div> ) } Set column width /** @jsxImportSource @emotion/react */ import { css } from '@emotion/react' import { useCallback, useMemo, useRef, useState } from 'react' import { AgGridReact } from 'ag-grid-react' import 'ag-grid-community/styles/ag-grid.css' import 'ag-grid-community/styles/ag-theme-alpine.css' import { useData } from './useData' const getColumnDefs = () => { return [ { field: 'athlete', width: 150, sort: 'asc' }, { field: 'age' }, { field: 'country', pinned: 'left' }, { field: 'sport' }, { field: 'year' }, { field: 'date' }, { field: 'gold' }, { field: 'silver' }, { field: 'bronze' }, { field: 'total' } ] } export const Example5 = () => { const gridRef = useRef() const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []) const { data: rowData } = useData() const defaultColDef = useMemo(() => { return { initialWidth: 100, width: 100, sortable: true, resizable: true, pinned: null, sort: null // important - clears sort if not specified in col def } }, []) const [columnDefs, setColumnDefs] = useState(getColumnDefs()) const onBtWithState = useCallback(() => { gridRef.current.api.setColumnDefs(getColumnDefs()) }, []) const onBtRemove = useCallback(() => { gridRef.current.api.setColumnDefs([]) }, []) return ( <div css={css` .test-grid { flex-grow: 1; } .test-container { height: 300px; display: flex; flex-direction: column; } .test-header { font-family: Verdana, Geneva, Tahoma, sans-serif; font-size: 13px; margin-bottom: 1rem; } `} > <div className='test-container'> <div className='test-header'> <button onClick={onBtWithState}>Set Columns with State</button> <button onClick={onBtRemove}>Remove Columns</button> </div> <div style={gridStyle} className='ag-theme-alpine'> <AgGridReact ref={gridRef} rowData={rowData} defaultColDef={defaultColDef} columnDefs={columnDefs} /> </div> </div> </div> ) } Column events /** @jsxImportSource @emotion/react */ import { useCallback, useMemo, useRef, useState } from 'react' import { AgGridReact } from 'ag-grid-react' import 'ag-grid-enterprise' import 'ag-grid-community/styles/ag-grid.css' import 'ag-grid-community/styles/ag-theme-alpine.css' import { css } from '@emotion/react' import { useData } from './useData' const colDefs = [ { field: 'athlete' }, { field: 'age' }, { field: 'country' }, { field: 'sport' }, { field: 'gold' }, { field: 'silver', sort: 'desc' }, { field: 'bronze' } ] export const Example6 = () => { const gridRef = useRef() const containerStyle = useMemo(() => ({ width: '100%', height: '400px' }), []) const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []) const { data: rowData } = useData() const defaultColDef = useMemo(() => { return { sortable: true, resizable: true, width: 150, enableRowGroup: true, enablePivot: true, enableValue: true } }, []) const [columnDefs, setColumnDefs] = useState(colDefs) const onSortChanged = useCallback((e) => window.alert('Event Sort Changed', e), []) const onColumnResized = useCallback((e) => window.alert('Event Column Resized', e), []) const onColumnVisible = useCallback((e) => window.alert('Event Column Visible', e), []) const onColumnPivotChanged = useCallback((e) => window.alert('Event Pivot Changed', e), []) const onColumnRowGroupChanged = useCallback((e) => window.alert('Event Row Group Changed', e), []) const onColumnValueChanged = useCallback((e) => window.alert('Event Value Changed', e), []) const onColumnMoved = useCallback((e) => window.alert('Event Column Moved', e), []) const onColumnPinned = useCallback((e) => window.alert('Event Column Pinned', e), []) const onBtSortOn = useCallback(() => { colDefs.forEach(function (colDef) { if (colDef.field === 'age') colDef.sort = 'desc' if (colDef.field === 'athlete') colDef.sort = 'asc' }) gridRef.current.api.setColumnDefs(colDefs) }, []) const onBtSortOff = useCallback(() => { colDefs.forEach(function (colDef) { colDef.sort = null }) gridRef.current.api.setColumnDefs(colDefs) }, []) const onBtWidthNarrow = useCallback(() => { colDefs.forEach(function (colDef) { if (colDef.field === 'age' || colDef.field === 'athlete') { colDef.width = 100 } }) gridRef.current.api.setColumnDefs(colDefs) }, []) const onBtWidthNormal = useCallback(() => { colDefs.forEach(colDef => { colDef.width = 200 }) gridRef.current.api.setColumnDefs(colDefs) }, []) const onBtHide = useCallback(() => { colDefs.forEach(function (colDef) { if (colDef.field === 'age' || colDef.field === 'athlete') { colDef.hide = true } }) gridRef.current.api.setColumnDefs(colDefs) }, []) const onBtShow = useCallback(() => { colDefs.forEach(function (colDef) { colDef.hide = false }) gridRef.current.api.setColumnDefs(colDefs) }, []) const onBtPivotOn = useCallback(() => { gridRef.current.columnApi.setPivotMode(true) colDefs.forEach(function (colDef) { if (colDef.field === 'country') { colDef.pivot = true } }) gridRef.current.api.setColumnDefs(colDefs) }, []) const onBtPivotOff = useCallback(() => { gridRef.current.columnApi.setPivotMode(false) colDefs.forEach(function (colDef) { colDef.pivot = false }) gridRef.current.api.setColumnDefs(colDefs) }, []) const onBtRowGroupOn = useCallback(() => { colDefs.forEach(function (colDef) { if (colDef.field === 'sport') { colDef.rowGroup = true } }) gridRef.current.api.setColumnDefs(colDefs) }, []) const onBtRowGroupOff = useCallback(() => { colDefs.forEach(function (colDef) { colDef.rowGroup = false }) gridRef.current.api.setColumnDefs(colDefs) }, []) const onBtAggFuncOn = useCallback(() => { colDefs.forEach(function (colDef) { if (colDef.field === 'gold' || colDef.field === 'silver' || colDef.field === 'bronze') { colDef.aggFunc = 'sum' } }) gridRef.current.api.setColumnDefs(colDefs) }, []) const onBtAggFuncOff = useCallback(() => { colDefs.forEach(function (colDef) { colDef.aggFunc = null }) gridRef.current.api.setColumnDefs(colDefs) }, []) const onBtPinnedOn = useCallback(() => { colDefs.forEach(function (colDef) { if (colDef.field === 'athlete') { colDef.pinned = 'left' } if (colDef.field === 'age') { colDef.pinned = 'right' } }) gridRef.current.api.setColumnDefs(colDefs) }, []) const onBtPinnedOff = useCallback(() => { colDefs.forEach(function (colDef) { colDef.pinned = null }) gridRef.current.api.setColumnDefs(colDefs) }, []) return ( <div style={containerStyle} css={css` .test-container { height: 400px; display: flex; flex-direction: column; } .test-header { font-family: Verdana, Geneva, Tahoma, sans-serif; font-size: 13px; margin-bottom: 0.5rem; } .test-header .example-section { margin-bottom: 0.5rem; } .test-button-group { display: inline-block; margin-right: 20px; margin-bottom: 10px; } .test-button-group button { margin: 4px; } #myGrid { flex: 1 1 0px; } `} > <div className='test-container'> <div className='test-header'> <div className='test-button-row'> <div className='test-button-group'> <button onClick={onBtSortOn}>Sort On</button> <br /> <button onClick={onBtSortOff}>Sort Off</button> </div> <div className='test-button-group'> <button onClick={onBtWidthNarrow}>Width Narrow</button> <br /> <button onClick={onBtWidthNormal}>Width Normal</button> </div> <div className='test-button-group'> <button onClick={onBtHide}>Hide Cols</button> <br /> <button onClick={onBtShow}>Show Cols</button> </div> <div className='test-button-group'> <button onClick={onBtPivotOn}>Pivot On</button> <br /> <button onClick={onBtPivotOff}>Pivot Off</button> </div> <div className='test-button-group'> <button onClick={onBtRowGroupOn}>Row Group On</button> <br /> <button onClick={onBtRowGroupOff}>Row Group Off</button> </div> <div className='test-button-group'> <button onClick={onBtAggFuncOn}>Agg Func On</button> <br /> <button onClick={onBtAggFuncOff}>Agg Func Off</button> </div> <div className='test-button-group'> <button onClick={onBtPinnedOn}>Pinned On</button> <br /> <button onClick={onBtPinnedOff}>Pinned Off</button> </div> </div> </div> <div style={gridStyle} className='ag-theme-alpine'> <AgGridReact ref={gridRef} rowData={rowData} defaultColDef={defaultColDef} columnDefs={columnDefs} // onGridReady={onGridReady} onSortChanged={onSortChanged} onColumnResized={onColumnResized} onColumnVisible={onColumnVisible} onColumnPivotChanged={onColumnPivotChanged} onColumnRowGroupChanged={onColumnRowGroupChanged} onColumnValueChanged={onColumnValueChanged} onColumnMoved={onColumnMoved} onColumnPinned={onColumnPinned} /> </div> </div> </div> ) } Multi column sort with command /** @jsxImportSource @emotion/react */ import { useCallback, useMemo, useState } from 'react' import { AgGridReact } from 'ag-grid-react' import 'ag-grid-community/styles/ag-grid.css' import 'ag-grid-community/styles/ag-theme-alpine.css' import { useData } from './useData' export const Example7 = () => { const containerStyle = useMemo(() => ({ width: '100%', height: '400px' }), []) const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []) const { data: rowData } = useData() const [columnDefs, setColumnDefs] = useState([ { field: 'athlete' }, { field: 'age', width: 100 }, { field: 'country' }, { field: 'year', width: 100 }, { field: 'date', sortingOrder: ['desc', null] }, { field: 'sport', sortingOrder: ['asc'] }, { field: 'gold', sortingOrder: ['asc', 'desc'] }, { field: 'silver' }, { field: 'bronze' }, { field: 'total' } ]) const defaultColDef = useMemo(() => { return { width: 170, sortable: true } }, []) const onGridReady = useCallback((params) => { const defaultSortModel = [ { colId: 'country', sort: 'asc', sortIndex: 0 }, { colId: 'athlete', sort: 'asc', sortIndex: 1 } ] params.columnApi.applyColumnState({ state: defaultSortModel }) }, []) return ( <div style={containerStyle}> <div style={gridStyle} className='ag-theme-alpine'> <AgGridReact rowData={rowData} columnDefs={columnDefs} defaultColDef={defaultColDef} multiSortKey='ctrl' onGridReady={onGridReady} animateRows // alwaysMultiSort /> </div> </div> ) } Wrap text and auto height row /** @jsxImportSource @emotion/react */ import { useCallback, useMemo, useState } from 'react' import { AgGridReact } from 'ag-grid-react' import 'ag-grid-enterprise' import 'ag-grid-community/styles/ag-grid.css' import 'ag-grid-community/styles/ag-theme-alpine.css' function getData () { const latinSentence = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu.' const latinWords = latinSentence.split(' ') const rowData = [] function generateRandomSentence (row, col) { const wordCount = ((row + 1) * (col + 1) * 733 * 19) % latinWords.length const parts = [] for (let i = 0; i < wordCount; i++) { parts.push(latinWords[i]) } const sentence = parts.join(' ') return sentence + '.' } // create 100 rows for (let i = 0; i < 100; i++) { const item = { rowNumber: 'Row ' + i, autoA: generateRandomSentence(i, 1), autoB: generateRandomSentence(i, 2), autoC: generateRandomSentence(i, 3) } rowData.push(item) } return rowData } export const Example8 = () => { const containerStyle = useMemo(() => ({ width: '100%', height: '400px' }), []) const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []) const [rowData, setRowData] = useState() const [columnDefs, setColumnDefs] = useState([ { headerName: 'Row #', field: 'rowNumber', width: 120 }, { field: 'autoA', width: 300, wrapText: true, autoHeight: true, headerName: 'A) Auto Height' }, { width: 300, field: 'autoB', wrapText: true, headerName: 'B) Normal Height' } ]) const defaultColDef = useMemo(() => { return { sortable: true, resizable: true } }, []) const sideBar = useMemo(() => { return { toolPanels: [ { id: 'columns', labelDefault: 'Columns', labelKey: 'columns', iconKey: 'columns', toolPanel: 'agColumnsToolPanel', toolPanelParams: { suppressRowGroups: true, suppressValues: true, suppressPivots: true, suppressPivotMode: true, suppressSideButtons: true, suppressColumnFilter: true, suppressColumnSelectAll: true, suppressColumnExpandAll: true } } ], defaultToolPanel: 'columns' } }, []) const onGridReady = useCallback((params) => { // in this example, the CSS styles are loaded AFTER the grid is created, // so we put this in a timeout, so height is calculated after styles are applied. setTimeout(function () { setRowData(getData()) }, 500) }, []) return ( <div style={containerStyle}> <div style={gridStyle} className='ag-theme-alpine'> <AgGridReact rowData={rowData} columnDefs={columnDefs} defaultColDef={defaultColDef} sideBar={sideBar} onGridReady={onGridReady} /> </div> </div> ) } Row drag /** @jsxImportSource @emotion/react */ import { useMemo, useState } from 'react' import { AgGridReact } from 'ag-grid-react' import 'ag-grid-community/styles/ag-grid.css' import 'ag-grid-community/styles/ag-theme-alpine.css' import { useData } from './useData' export const Example9 = () => { const containerStyle = useMemo(() => ({ width: '100%', height: '400px' }), []) const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []) const { data: rowData } = useData() const [columnDefs, setColumnDefs] = useState([ { field: 'athlete', rowDrag: true }, { field: 'country' }, { field: 'year', width: 100 }, { field: 'date' }, { field: 'sport' }, { field: 'gold' }, { field: 'silver' }, { field: 'bronze' } ]) const defaultColDef = useMemo(() => { return { width: 170, sortable: true, filter: true } }, []) return ( <div style={containerStyle}> <div style={gridStyle} className='ag-theme-alpine'> <AgGridReact rowData={rowData} columnDefs={columnDefs} defaultColDef={defaultColDef} rowDragManaged animateRows // onGridReady={onGridReady} /> </div> </div> ) } Data from selected row /** @jsxImportSource @emotion/react */ import { useCallback, useMemo, useRef, useState } from 'react' import { AgGridReact } from 'ag-grid-react' import 'ag-grid-community/styles/ag-grid.css' import 'ag-grid-community/styles/ag-theme-alpine.css' import { useData } from './useData' export const Example10 = () => { const gridRef = useRef() const containerStyle = useMemo(() => ({ width: '100%', height: '400px' }), []) const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []) const { data: rowData } = useData() const [columnDefs, setColumnDefs] = useState([ { field: 'athlete', minWidth: 150 }, { field: 'age', maxWidth: 90 }, { field: 'country', minWidth: 150 } ]) const defaultColDef = useMemo(() => { return { flex: 1, minWidth: 100 } }, []) const onSelectionChanged = useCallback(() => { const selectedRows = gridRef.current.api.getSelectedRows() document.querySelector('#selectedRows').innerHTML = selectedRows.length === 1 ? selectedRows[0].athlete : '' }, []) return ( <div style={containerStyle}> <div css={{ display: 'flex', flexDirection: 'column', height: '100%' }} > <div className='example-header'> Selection: <span id='selectedRows' /> </div> <div style={gridStyle} className='ag-theme-alpine'> <AgGridReact ref={gridRef} rowData={rowData} columnDefs={columnDefs} defaultColDef={defaultColDef} rowSelection='single' // onGridReady={onGridReady} onSelectionChanged={onSelectionChanged} /> </div> </div> </div> ) } External filter /** @jsxImportSource @emotion/react */ import { useCallback, useMemo, useRef, useState } from 'react' import { AgGridReact } from 'ag-grid-react' import 'ag-grid-enterprise' import 'ag-grid-community/styles/ag-grid.css' import 'ag-grid-community/styles/ag-theme-alpine.css' import { css } from '@emotion/react' import { useData } from './useData' const dateFilterParams = { comparator: (filterLocalDateAtMidnight, cellValue) => { const cellDate = asDate(cellValue) if (filterLocalDateAtMidnight.getTime() === cellDate.getTime()) return 0 if (cellDate < filterLocalDateAtMidnight) return -1 if (cellDate > filterLocalDateAtMidnight) return 1 } } let ageType = 'everyone' const asDate = (dateAsString) => { const splitFields = dateAsString.split('/') return new Date( Number.parseInt(splitFields[2]), Number.parseInt(splitFields[1]) - 1, Number.parseInt(splitFields[0]) ) } export const Example11 = () => { const gridRef = useRef() const containerStyle = useMemo(() => ({ width: '100%', height: '400px' }), []) const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []) const { data: rowData } = useData() const [columnDefs, setColumnDefs] = useState([ { field: 'athlete', minWidth: 180 }, { field: 'age', filter: 'agNumberColumnFilter', maxWidth: 80 }, { field: 'country' }, { field: 'year', maxWidth: 90 }, { field: 'date', filter: 'agDateColumnFilter', filterParams: dateFilterParams }, { field: 'gold', filter: 'agNumberColumnFilter' }, { field: 'silver', filter: 'agNumberColumnFilter' }, { field: 'bronze', filter: 'agNumberColumnFilter' } ]) const defaultColDef = useMemo(() => { return { flex: 1, minWidth: 120, filter: true } }, []) const externalFilterChanged = useCallback((newValue) => { ageType = newValue gridRef.current.api.onFilterChanged() }, []) const isExternalFilterPresent = useCallback(() => { // if ageType is not everyone, then we are filtering return ageType !== 'everyone' }, []) const doesExternalFilterPass = useCallback( (node) => { if (node.data) { switch (ageType) { case 'below25': return node.data.age < 25 case 'between25and50': return node.data.age >= 25 && node.data.age <= 50 case 'above50': return node.data.age > 50 case 'dateAfter2008': return asDate(node.data.date) > new Date(2008, 1, 1) default: return true } } return true }, [ageType] ) return ( <div style={containerStyle} css={css` .test-container { height: 100%; display: flex; flex-direction: column; } .test-header { font-family: Verdana, Geneva, Tahoma, sans-serif; font-size: 13px; margin-bottom: 10px; display: flex; justify-content: space-around; border: 1px solid grey; padding: 10px; border-radius: 5px; } `} > <div className='test-container'> <div className='test-header'> <label> <input type='radio' name='filter' id='everyone' onChange={() => externalFilterChanged('everyone')} /> Everyone </label> <label> <input type='radio' name='filter' id='below25' onChange={() => externalFilterChanged('below25')} /> Below 25 </label> <label> <input type='radio' name='filter' id='between25and50' onChange={() => externalFilterChanged('between25and50')} /> Between 25 and 50 </label> <label> <input type='radio' name='filter' id='above50' onChange={() => externalFilterChanged('above50')} /> Above 50 </label> <label> <input type='radio' name='filter' id='dateAfter2008' onChange={() => externalFilterChanged('dateAfter2008')} /> After 01/01/2008 </label> </div> <div style={gridStyle} className='ag-theme-alpine'> <AgGridReact ref={gridRef} rowData={rowData} columnDefs={columnDefs} defaultColDef={defaultColDef} animateRows isExternalFilterPresent={isExternalFilterPresent} doesExternalFilterPass={doesExternalFilterPass} // onGridReady={onGridReady} /> </div> </div> </div> ) } Quick filter /** @jsxImportSource @emotion/react */ import { useCallback, useMemo, useRef, useState } from 'react' import { AgGridReact } from 'ag-grid-react' import 'ag-grid-community/styles/ag-grid.css' import 'ag-grid-community/styles/ag-theme-alpine.css' import { css } from '@emotion/react' function getData () { const rowData = [ { name: 'Michael Phelps', person: { age: 23, country: 'United States' }, medals: { gold: 8, silver: 0, bronze: 0 } }, { name: 'Michael Phelps', person: { age: 19, country: 'United States' }, medals: { gold: 6, silver: 0, bronze: 2 } }, { name: 'Michael Phelps', person: { age: 27, country: 'United States' }, medals: { gold: 4, silver: 2, bronze: 0 } }, ] return rowData } const getMedalString = function ({ gold, silver, bronze }) { const goldStr = gold > 0 ? `Gold: ${gold} ` : '' const silverStr = silver > 0 ? `Silver: ${silver} ` : '' const bronzeStr = bronze > 0 ? `Bronze: ${bronze}` : '' return goldStr + silverStr + bronzeStr } const MedalRenderer = function (params) { return getMedalString(params.value) } export const Example12 = () => { const gridRef = useRef() const containerStyle = useMemo(() => ({ width: '100%', height: '400px' }), []) const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []) const [rowData, setRowData] = useState(getData()) const [columnDefs, setColumnDefs] = useState([ // simple column, easy to understand { field: 'name' }, // the grid works with embedded fields { headerName: 'Age', field: 'person.age' }, // or use value getter, all works with quick filter { headerName: 'Country', valueGetter: 'data.person.country' }, // or use the object value, so value passed around is an object { headerName: 'Results', field: 'medals', cellRenderer: MedalRenderer, // this is needed to avoid toString=[object,object] result with objects getQuickFilterText: (params) => { return getMedalString(params.value) } } ]) const defaultColDef = useMemo(() => { return { flex: 1, editable: true } }, []) const onFilterTextBoxChanged = useCallback(() => { gridRef.current.api.setQuickFilter( document.getElementById('filter-text-box').value ) }, []) const onPrintQuickFilterTexts = useCallback(() => { gridRef.current.api.forEachNode(function (rowNode, index) { console.log('Row ' + index + ' quick filter text is ' + rowNode.quickFilterAggregateText) }) }, []) return ( <div style={containerStyle} css={css` .example-wrapper { display: flex; flex-direction: column; height: 100%; } #myGrid { flex: 1 1 0px; width: 100%; } .example-header { font-family: Verdana, Geneva, Tahoma, sans-serif; font-size: 13px; margin-bottom: 5px; } `} > <div className='example-wrapper'> <div className='example-header'> <input type='text' id='filter-text-box' placeholder='Filter...' onInput={onFilterTextBoxChanged} /> <button style={{ marginLeft: '20px' }} onClick={onPrintQuickFilterTexts} > Print Quick Filter Cache Texts </button> </div> <div style={gridStyle} className='ag-theme-alpine'> <AgGridReact ref={gridRef} rowData={rowData} columnDefs={columnDefs} defaultColDef={defaultColDef} cacheQuickFilter /> </div> </div> </div> ) } valueGetter /** @jsxImportSource @emotion/react */ import { useMemo, useState } from 'react' import { AgGridReact } from 'ag-grid-react' import 'ag-grid-community/styles/ag-grid.css' import 'ag-grid-community/styles/ag-theme-alpine.css' import { css } from '@emotion/css' const hashValueGetter = (params) => params.node ? params.node.rowIndex : null const abValueGetter = (params) => params.data.a + params.data.b const a1000ValueGetter = (params) => params.data.a * 1000 const b137ValueGetter = (params) => params.data.b * 137 const randomValueGetter = () => Math.floor(Math.random() * 1000) const chainValueGetter = (params) => params.getValue('a&b') * 1000 const constValueGetter = () => 99999 const createRowData = () => { const rowData = [] for (let i = 0; i < 100; i++) { rowData.push({ a: Math.floor(i % 4), b: Math.floor(i % 7) }) } return rowData } export const Example13 = () => { const containerStyle = useMemo(() => ({ width: '100%', height: '400px' }), []) const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []) const [rowData, setRowData] = useState(createRowData()) const [columnDefs, setColumnDefs] = useState([ { headerName: '#', maxWidth: 100, valueGetter: hashValueGetter }, { field: 'a' }, { field: 'b' }, { headerName: 'A + B', colId: 'a&b', valueGetter: abValueGetter }, { headerName: 'A * 1000', minWidth: 95, valueGetter: a1000ValueGetter }, { headerName: 'B * 137', minWidth: 90, valueGetter: b137ValueGetter }, { headerName: 'Random', minWidth: 90, valueGetter: randomValueGetter }, { headerName: 'Chain', valueGetter: chainValueGetter }, { headerName: 'Const', minWidth: 85, valueGetter: constValueGetter } ]) const defaultColDef = useMemo(() => { return { flex: 1, minWidth: 75 // cellClass: 'number-cell' } }, []) return ( <div style={containerStyle} css={css` .number-cell { text-align: right; } `} > <div style={gridStyle} className='ag-theme-alpine-dark'> <AgGridReact rowData={rowData} columnDefs={columnDefs} defaultColDef={defaultColDef} /> </div> </div> ) } valueFormatter /** @jsxImportSource @emotion/react */ import { useMemo, useState } from 'react' import { AgGridReact } from 'ag-grid-react' import 'ag-grid-community/styles/ag-grid.css' import 'ag-grid-community/styles/ag-theme-alpine.css' const bracketsFormatter = (params) => { return '(' + params.value + ')' } const currencyFormatter = (params) => '£' + formatNumber(params.value) const formatNumber = (number) => { // this puts commas into the number eg 1000 goes to 1,000, // i pulled this from stack overflow, i have no idea how it works return Math.floor(number) .toString() .replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,') } const createRowData = () => { const rowData = [] for (let i = 0; i < 100; i++) { rowData.push({ a: Math.floor(((i + 2) * 173456) % 10000), b: Math.floor(((i + 7) * 373456) % 10000) }) } return rowData } export const Example14 = () => { const containerStyle = useMemo(() => ({ width: '100%', height: '400px' }), []) const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []) const [rowData, setRowData] = useState(createRowData()) const [columnDefs, setColumnDefs] = useState([ { headerName: 'A', field: 'a' }, { headerName: 'B', field: 'b' }, { headerName: '£A', field: 'a', valueFormatter: currencyFormatter }, { headerName: '£B', field: 'b', valueFormatter: currencyFormatter }, { headerName: '(A)', field: 'a', valueFormatter: bracketsFormatter }, { headerName: '(B)', field: 'b', valueFormatter: bracketsFormatter } ]) const defaultColDef = useMemo(() => { return { flex: 1, cellClass: 'number-cell', resizable: true } }, []) return ( <div style={containerStyle}> <div style={gridStyle} className='ag-theme-alpine-dark'> <AgGridReact rowData={rowData} columnDefs={columnDefs} defaultColDef={defaultColDef} /> </div> </div> ) } SelectCellEditor, RichSelectCellEditor /** @jsxImportSource @emotion/react */ import { useCallback, useMemo, useState } from 'react' import { AgGridReact } from 'ag-grid-react' import 'ag-grid-enterprise' import 'ag-grid-community/styles/ag-grid.css' import 'ag-grid-community/styles/ag-theme-alpine.css' // data from server const dataFromServer = [ { make: 'tyt', exteriorColour: 'fg', interiorColour: 'bw', price: 35000 }, { make: 'frd', exteriorColour: 'bw', interiorColour: 'cb', price: 32000 }, { make: 'prs', exteriorColour: 'cb', interiorColour: 'fg', price: 72000 }, { make: 'tyt', exteriorColour: 'fg', interiorColour: 'bw', price: 35000 }, { make: 'frd', exteriorColour: 'bw', interiorColour: 'cb', price: 32000 }, { make: 'prs', exteriorColour: 'cb', interiorColour: 'fg', price: 72000 }, { make: 'tyt', exteriorColour: 'fg', interiorColour: 'bw', price: 35000 }, { make: 'frd', exteriorColour: 'bw', interiorColour: 'cb', price: 32000 }, { make: 'prs', exteriorColour: 'cb', interiorColour: 'fg', price: 72000 }, { make: 'tyt', exteriorColour: 'fg', interiorColour: 'bw', price: 35000 }, { make: 'frd', exteriorColour: 'bw', interiorColour: 'cb', price: 32000 }, { make: 'prs', exteriorColour: 'cb', interiorColour: 'fg', price: 72000 }, { make: 'tyt', exteriorColour: 'fg', interiorColour: 'bw', price: 35000 }, { make: 'frd', exteriorColour: 'bw', interiorColour: 'cb', price: 32000 }, { make: 'prs', exteriorColour: 'cb', interiorColour: 'fg', price: 72000 }, { make: 'prs', exteriorColour: 'cb', interiorColour: 'fg', price: 72000 }, { make: 'tyt', exteriorColour: 'fg', interiorColour: 'bw', price: 35000 }, { make: 'frd', exteriorColour: 'bw', interiorColour: 'cb', price: 32000 } ] const removeSpaces = (str) => str ? str.replace(/s/g, '') : str const ColourCellRenderer = ({ value, valueFormatted }) => ( <> { value === '(Select All)' ? <div>{value}</div> : <span style={{ color: removeSpaces(valueFormatted) }}>{valueFormatted}</span> } </> ) const carMappings = { tyt: 'Toyota', frd: 'Ford', prs: 'Porsche', nss: 'Nissan' } const colourMappings = { cb: 'Cadet Blue', bw: 'Burlywood', fg: 'Forest Green' } const carBrands = Object.keys(carMappings) const colours = Object.keys(colourMappings) const lookupValue = (mappings, key) => mappings[key] const lookupKey = (mappings, name) => { const keys = Object.keys(mappings) for (let i = 0; i < keys.length; i++) { const key = keys[i] if (mappings[key] === name) return key } } const currencyFormatter = (params) => { const value = Math.floor(params.value) if (isNaN(value)) return '' return '£' + value.toString().replace(/(d)(?=(d{3})+(?!d))/g, '$1,') } const numberValueSetter = (params) => { if (isNaN(parseFloat(params.newValue)) || !isFinite(params.newValue)) { return false // don't set invalid numbers! } params.data.price = params.newValue return true } export const Example15 = () => { const containerStyle = useMemo(() => ({ width: '100%', height: '400px' }), []) const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []) const [rowData, setRowData] = useState(dataFromServer) const [columnDefs, setColumnDefs] = useState([ { field: 'make', cellEditor: 'agSelectCellEditor', cellEditorParams: { values: carBrands }, filterParams: { valueFormatter: (params) => lookupValue(carMappings, params.value) }, valueFormatter: (params) => lookupValue(carMappings, params.value) }, { field: 'exteriorColour', minWidth: 150, cellEditor: 'agRichSelectCellEditor', cellEditorPopup: true, cellEditorParams: { values: colours, cellRenderer: ColourCellRenderer }, filter: 'agSetColumnFilter', filterParams: { values: colours, valueFormatter: (params) => lookupValue(colourMappings, params.value), cellRenderer: ColourCellRenderer }, valueFormatter: (params) => lookupValue(colourMappings, params.value), valueParser: (params) => lookupKey(colourMappings, params.newValue), cellRenderer: ColourCellRenderer }, { field: 'interiorColour', minWidth: 150, cellEditor: 'agTextCellEditor', cellEditorParams: { useFormatter: true }, filter: 'agSetColumnFilter', filterParams: { values: colours, valueFormatter: (params) => lookupValue(colourMappings, params.value), cellRenderer: ColourCellRenderer }, valueFormatter: (params) => lookupValue(colourMappings, params.value), valueParser: (params) => lookupKey(colourMappings, params.newValue), cellRenderer: ColourCellRenderer }, { headerName: 'Retail Price', field: 'price', minWidth: 140, colId: 'retailPrice', valueGetter: (params) => params.data.price, valueFormatter: currencyFormatter, valueSetter: numberValueSetter }, { headerName: 'Retail Price (incl Taxes)', minWidth: 205, editable: false, valueGetter: (params) => { // example of chaining value getters return params.getValue('retailPrice') * 1.2 }, valueFormatter: currencyFormatter } ]) const defaultColDef = useMemo(() => { return { flex: 1, filter: true, editable: true } }, []) const onCellValueChanged = useCallback((params) => { // notice that the data always contains the keys rather than values after editing console.log('onCellValueChanged: ', params) }, []) return ( <div style={containerStyle}> <div style={gridStyle} className='ag-theme-alpine-dark'> <AgGridReact rowData={rowData} columnDefs={columnDefs} defaultColDef={defaultColDef} onCellValueChanged={onCellValueChanged} /> </div> </div> ) } Specific editable cells with styles /** @jsxImportSource @emotion/react */ import { useCallback, useMemo, useRef, useState } from 'react' import { AgGridReact } from 'ag-grid-react' import 'ag-grid-community/styles/ag-grid.css' import 'ag-grid-community/styles/ag-theme-alpine.css' import { css } from '@emotion/react' import { useData } from './useData' let editableYear = 2012 const isCellEditable = (params) => { return params.data.year === editableYear } export const Example16 = () => { const gridRef = useRef() const containerStyle = useMemo(() => ({ width: '100%', height: '400px' }), []) const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []) const { data: rowData } = useData() const [columnDefs, setColumnDefs] = useState([ { field: 'athlete', type: 'editableColumn' }, { field: 'age', type: 'editableColumn' }, { field: 'year' }, { field: 'country' }, { field: 'date' }, { field: 'sport' }, { field: 'gold' }, { field: 'silver' }, { field: 'bronze' }, { field: 'total' } ]) const columnTypes = useMemo(() => { return { editableColumn: { editable: (params) => { return isCellEditable(params) }, cellStyle: (params) => { if (isCellEditable(params)) { return { backgroundColor: 'lightBlue' } } } } } }, []) const setEditableYear = useCallback((year) => { editableYear = year // Redraw to re-apply the new cell style gridRef.current.api.redrawRows() }, []) return ( <div style={containerStyle} css={css` .example-wrapper { display: flex; flex-direction: column; height: 100%; } #myGrid { flex: 1 1 0px; width: 100%; } `} > <div className='example-wrapper'> <div style={{ marginBottom: '5px' }}> <button style={{ fontSize: '12px' }} onClick={() => setEditableYear(2008)} > Enable Editing for 2008 </button> <button style={{ fontSize: '12px' }} onClick={() => setEditableYear(2012)} > Enable Editing for 2012 </button> </div> <div style={gridStyle} className='ag-theme-alpine'> <AgGridReact ref={gridRef} rowData={rowData} columnDefs={columnDefs} columnTypes={columnTypes} // onGridReady={onGridReady} /> </div> </div> </div> ) } Edit cell /** @jsxImportSource @emotion/react */ import { useCallback, useMemo, useRef, useState } from 'react' import { AgGridReact } from 'ag-grid-react' import 'ag-grid-community/styles/ag-grid.css' import 'ag-grid-community/styles/ag-theme-alpine.css' import { css } from '@emotion/react' function getData () { return [ { firstName: 'Bob', lastName: 'Harrison', gender: 'Male', address: '1197 Thunder Wagon Common, Cataract, RI, 02987-1016, US, (401) 747-0763', mood: 'Happy', country: 'Ireland' }, { firstName: 'Bob', lastName: 'Harrison', gender: 'Male', address: '1197 Thunder Wagon Common, Cataract, RI, 02987-1016, US, (401) 747-0763', mood: 'Happy', country: 'Ireland' }, { firstName: 'Mary', lastName: 'Wilson', gender: 'Female', age: 11, address: '3685 Rocky Glade, Showtucket, NU, X1E-9I0, CA, (867) 371-4215', mood: 'Sad', country: 'Ireland' }, { firstName: 'Zahid', lastName: 'Khan', gender: 'Male', age: 12, address: '3235 High Forest, Glen Campbell, MS, 39035-6845, US, (601) 638-8186', mood: 'Happy', country: 'Ireland' }, { firstName: 'Jerry', lastName: 'Mane', gender: 'Male', age: 12, address: '2234 Sleepy Pony Mall , Drain, DC, 20078-4243, US, (202) 948-3634', mood: 'Happy', country: 'Ireland' } ] } const getPinnedTopData = () => { return [ { firstName: '##', lastName: '##', gender: '##', address: '##', mood: '##', country: '##' } ] } const getPinnedBottomData = () => { return [ { firstName: '##', lastName: '##', gender: '##', address: '##', mood: '##', country: '##' } ] } export const Example17 = () => { const gridRef = useRef() const containerStyle = useMemo(() => ({ width: '100%', height: '400px' }), []) const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []) const [rowData, setRowData] = useState(getData()) const [columnDefs, setColumnDefs] = useState([ { field: 'firstName' }, { field: 'lastName' }, { field: 'gender' }, { field: 'age' }, { field: 'mood' }, { field: 'country' }, { field: 'address', minWidth: 550 } ]) const defaultColDef = useMemo(() => { return { flex: 1, minWidth: 110, editable: true, resizable: true } }, []) const pinnedTopRowData = useMemo(() => { return getPinnedTopData() }, []) const pinnedBottomRowData = useMemo(() => { return getPinnedBottomData() }, []) const onRowEditingStarted = useCallback((event) => { console.log('never called - not doing row editing') }, []) const onRowEditingStopped = useCallback((event) => { console.log('never called - not doing row editing') }, []) const onCellEditingStarted = useCallback((event) => { console.log('cellEditingStarted') }, []) const onCellEditingStopped = useCallback((event) => { console.log('cellEditingStopped') }, []) const onBtStopEditing = useCallback(() => { gridRef.current.api.stopEditing() }, []) const onBtStartEditing = useCallback((key, char, pinned) => { gridRef.current.api.setFocusedCell(0, 'lastName', pinned) gridRef.current.api.startEditingCell({ rowIndex: 0, colKey: 'lastName', // set to 'top', 'bottom' or undefined rowPinned: pinned, key, charPress: char }) }, []) const onBtNextCell = useCallback(() => { gridRef.current.api.tabToNextCell() }, []) const onBtPreviousCell = useCallback(() => { gridRef.current.api.tabToPreviousCell() }, []) const onBtWhich = useCallback(() => { const cellDefs = gridRef.current.api.getEditingCells() if (cellDefs.length > 0) { const cellDef = cellDefs[0] window.alert('editing cell is: row = ' + cellDef.rowIndex + ', col = ' + cellDef.column.getId() + ', floating = ' + cellDef.rowPinned) } else { window.alert('no cells are editing') } }, []) return ( <div style={containerStyle} css={ css` .example-wrapper { display: flex; flex-direction: column; height: 100%; } .grid-wrapper { flex: 1 1 0px; } #myGrid { height: 100%; width: 100%; } ` } > <div className='example-wrapper'> <div style={{ marginBottom: '5px', display: 'flex', justifyContent: 'space-between' }} > <div> <button onClick={() => onBtStartEditing(undefined)}> edit (0) </button> <button onClick={() => onBtStartEditing('Backspace')}> edit (0, Backspace) </button> <button onClick={() => onBtStartEditing(undefined, 'T')}> edit (0, 'T') </button> <button onClick={() => onBtStartEditing(undefined, undefined, 'top')} > edit (0, Top) </button> <button onClick={() => onBtStartEditing(undefined, undefined, 'bottom')} > edit (0, Bottom) </button> </div> <div> <button onClick={onBtStopEditing}>stop ()</button> <button onClick={onBtNextCell}>next ()</button> <button onClick={onBtPreviousCell}>previous ()</button> </div> <div> <button onClick={onBtWhich}>which ()</button> </div> </div> <div className='grid-wrapper'> <div style={gridStyle} className='ag-theme-alpine'> <AgGridReact ref={gridRef} rowData={rowData} columnDefs={columnDefs} defaultColDef={defaultColDef} pinnedTopRowData={pinnedTopRowData} pinnedBottomRowData={pinnedBottomRowData} onRowEditingStarted={onRowEditingStarted} onRowEditingStopped={onRowEditingStopped} onCellEditingStarted={onCellEditingStarted} onCellEditingStopped={onCellEditingStopped} /> </div> </div> </div> </div> ) } Parse value during editing /** @jsxImportSource @emotion/react */ import { useCallback, useMemo, useRef, useState } from 'react' import { AgGridReact } from 'ag-grid-react' import 'ag-grid-community/styles/ag-grid.css' import 'ag-grid-community/styles/ag-theme-alpine.css' function getData () { const rowData = [] const words = [ 'One', 'Apple', 'Moon', 'Sugar', 'Grid', 'Banana', 'Sunshine', 'Stars', 'Black', 'White', 'Salt', 'Beach' ] for (let i = 0; i < 100; i++) { rowData.push({ simple: words[i % words.length], number: Math.floor(((i + 2) * 173456) % 10000) }) } return rowData } const numberParser = (params) => { return 'num is doubled after editing ' + 2 * Number(params.newValue) } export const Example18 = () => { const gridRef = useRef() const containerStyle = useMemo(() => ({ width: '100%', height: '400px' }), []) const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []) const [rowData, setRowData] = useState(getData()) const [columnDefs, setColumnDefs] = useState([ { headerName: 'Name', field: 'simple' }, { headerName: 'number', field: 'number', valueParser: numberParser } ]) const defaultColDef = useMemo(() => { return { flex: 1, editable: true, resizable: true } }, []) const onGridReady = useCallback((params) => { gridRef.current.api.sizeColumnsToFit() }, []) const onCellValueChanged = useCallback((event) => { console.log('data after changes is: ', event.data) }, []) return ( <div style={containerStyle}> <div style={gridStyle} className='ag-theme-alpine-dark'> <AgGridReact ref={gridRef} rowData={rowData} columnDefs={columnDefs} defaultColDef={defaultColDef} onGridReady={onGridReady} onCellValueChanged={onCellValueChanged} /> </div> </div> ) } Pagination + checkboxSelection /** @jsxImportSource @emotion/react */ import { useMemo, useState } from 'react' import { AgGridReact } from 'ag-grid-react' import 'ag-grid-enterprise' import 'ag-grid-community/styles/ag-grid.css' import 'ag-grid-community/styles/ag-theme-alpine.css' import { useData } from './useData' const checkboxSelection = function (params) { // we put checkbox on the name if we are not doing grouping return params.columnApi.getRowGroupColumns().length === 0 } const headerCheckboxSelection = function (params) { // we put checkbox on the name if we are not doing grouping return params.columnApi.getRowGroupColumns().length === 0 } export const Example19 = () => { const containerStyle = useMemo(() => ({ width: '100%', height: '400px' }), []) const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []) const { data: rowData } = useData() const [columnDefs, setColumnDefs] = useState([ { field: 'athlete', minWidth: 170, checkboxSelection, headerCheckboxSelection }, { field: 'age' }, { field: 'country' }, { field: 'year' }, { field: 'date' }, { field: 'sport' }, { field: 'gold' }, { field: 'silver' }, { field: 'bronze' }, { field: 'total' } ]) const autoGroupColumnDef = useMemo(() => { return { headerName: 'Group', minWidth: 170, field: 'athlete', valueGetter: (params) => { if (params.node.group) { return params.node.key } else { return params.data[params.colDef.field] } }, headerCheckboxSelection: true, cellRenderer: 'agGroupCellRenderer', cellRendererParams: { checkbox: true } } }, []) const defaultColDef = useMemo(() => { return { editable: true, enableRowGroup: true, enablePivot: true, enableValue: true, sortable: true, resizable: true, filter: true, flex: 1, minWidth: 100 } }, []) return ( <div style={containerStyle}> <div style={gridStyle} className='ag-theme-alpine'> <AgGridReact rowData={rowData} columnDefs={columnDefs} autoGroupColumnDef={autoGroupColumnDef} defaultColDef={defaultColDef} suppressRowClickSelection groupSelectsChildren rowSelection='multiple' rowGroupPanelShow='always' pivotPanelShow='always' pagination // onGridReady={onGridReady} /> </div> </div> ) } Limit number of characters in cell const columnDefs = [ ... { field: 'value.name', headerName: 'Name', width: 250, minWidth: 250, wrapText: true, autoHeight: true, flex: 2, cellStyle: { justifyContent: 'center', textAlign: 'center' } // here we limit number of characters in editable cell editable: true, cellEditor: 'agTextCellEditor', cellEditorParams: { maxLength: 10 }, }, ]