Row data is the information displayed in your grid. AG Grid provides flexible APIs for loading, updating, and managing row data efficiently.
Overview
Row data is provided via the rowData property in GridOptions. Each item in the array represents one row:
const gridOptions : GridOptions = {
rowData: [
{ athlete: 'Michael Phelps' , age: 23 , country: 'United States' },
{ athlete: 'Usain Bolt' , age: 22 , country: 'Jamaica' },
{ athlete: 'Katie Ledecky' , age: 19 , country: 'United States' }
]
};
Setting Row Data
Initial Load
const gridOptions : GridOptions = {
columnDefs: [ ... ],
rowData: myData
};
Async Data Loading
const gridOptions : GridOptions = {
columnDefs: [ ... ],
rowData: [] // Start with empty array
};
// Load data asynchronously
fetch ( '/api/athletes' )
. then ( response => response . json ())
. then ( data => {
api . setGridOption ( 'rowData' , data );
});
Row IDs
Providing Row IDs
Provide unique IDs for efficient updates and transactions:
const gridOptions : GridOptions = {
getRowId : ( params ) => params . data . id
};
Without getRowId, AG Grid auto-generates IDs based on row index, which can cause issues when data changes.
Why Row IDs Matter
Without Row IDs
With Row IDs
// Problem: Grid can't track which rows changed
const newData = [ ... oldData ];
newData [ 5 ] = updatedRow ;
api . setGridOption ( 'rowData' , newData );
// Grid re-renders everything!
Updating Row Data
Full Replacement
Replace all data at once:
// Replace entire dataset
api . setGridOption ( 'rowData' , newData );
Transactions (Recommended)
For better performance, use transactions to update specific rows:
interface RowDataTransaction {
add ?: any []; // Rows to add
remove ?: any []; // Rows to remove
update ?: any []; // Rows to update
addIndex ?: number ; // Index to add rows at
}
Adding Rows
// Add rows to the end
api . applyTransaction ({
add: [
{ id: 101 , athlete: 'New Athlete 1' , age: 25 },
{ id: 102 , athlete: 'New Athlete 2' , age: 27 }
]
});
// Add rows at specific index
api . applyTransaction ({
add: [{ id: 103 , athlete: 'Inserted Athlete' , age: 24 }],
addIndex: 0 // Add at the beginning
});
Updating Rows
// Update existing rows (matches by row ID)
api . applyTransaction ({
update: [
{ id: 1 , athlete: 'Updated Name' , age: 24 }
]
});
For updates to work, you must provide getRowId so the grid can match rows by ID.
Removing Rows
// Remove rows (matches by row ID)
const rowsToRemove = [ rowData [ 0 ], rowData [ 5 ]];
api . applyTransaction ({
remove: rowsToRemove
});
Combined Transactions
// Perform multiple operations in one transaction
api . applyTransaction ({
add: [ newRow1 , newRow2 ],
update: [ updatedRow1 ],
remove: [ oldRow1 , oldRow2 ]
});
Async Transactions
For high-frequency updates, use async transactions:
// Queue multiple transactions
api . applyTransactionAsync ({ add: [ row1 ] });
api . applyTransactionAsync ({ add: [ row2 ] });
api . applyTransactionAsync ({ update: [ row3 ] });
// Grid batches these for efficiency
// Force execution of queued transactions
api . flushAsyncTransactions ();
Accessing Row Data
Get All Row Data
// Not recommended for large datasets
const allRows : any [] = [];
api . forEachNode ( node => allRows . push ( node . data ));
Get Displayed Rows
// Get rows after filtering/sorting
const displayedRows : any [] = [];
api . forEachNodeAfterFilterAndSort ( node => {
displayedRows . push ( node . data );
});
Get Specific Row
// Get row by ID
const rowNode = api . getRowNode ( '123' );
if ( rowNode ) {
console . log ( rowNode . data );
}
Get Selected Rows
const selectedRows = api . getSelectedRows ();
console . log ( 'Selected:' , selectedRows );
Row Nodes
Each row is represented internally by a RowNode object:
interface IRowNode < TData = any > {
/** The user-provided data */
data : TData ;
/** Unique ID for this row */
id : string ;
/** The row index (position in grid) */
rowIndex : number | null ;
/** Whether this row is selected */
selected : boolean ;
/** For tree data, the child rows */
childrenAfterGroup : IRowNode < TData >[] | null ;
/** For grouping, whether this is a group node */
group : boolean ;
/** Set data value for a column */
setDataValue ( colKey : string | Column , newValue : any ) : void ;
// ... many more properties and methods
}
Updating via Row Nodes
// Get the row node
const rowNode = api . getRowNode ( '123' );
// Update a single cell value
rowNode . setDataValue ( 'age' , 25 );
// Update entire row data
rowNode . setData ({ ... rowNode . data , age: 25 , country: 'USA' });
// Trigger refresh
api . refreshCells ({ rowNodes: [ rowNode ] });
Immutable Data
For React and other frameworks using immutable data:
const gridOptions : GridOptions = {
getRowId : ( params ) => params . data . id ,
// Immutable data is default behavior
};
// Update immutably
const newData = rowData . map ( row =>
row . id === targetId
? { ... row , age: 25 } // Create new object
: row
);
api . setGridOption ( 'rowData' , newData );
// Grid efficiently detects which rows changed
Common Patterns
Before/After: Simple Data Update
Before - Full Replacement
After - Transaction
// Inefficient: Replaces all data
const updatedData = [ ... rowData ];
const index = updatedData . findIndex ( r => r . id === targetId );
updatedData [ index ] = { ... updatedData [ index ], age: 25 };
api . setGridOption ( 'rowData' , updatedData );
Before/After: Adding Multiple Rows
Before - Multiple Operations
After - Batched Transaction
// Multiple re-renders
api . applyTransaction ({ add: [ row1 ] });
api . applyTransaction ({ add: [ row2 ] });
api . applyTransaction ({ add: [ row3 ] });
Real-Time Data Updates
// WebSocket example
const ws = new WebSocket ( 'wss://api.example.com/data' );
ws . onmessage = ( event ) => {
const update = JSON . parse ( event . data );
switch ( update . type ) {
case 'add' :
api . applyTransaction ({ add: [ update . data ] });
break ;
case 'update' :
api . applyTransaction ({ update: [ update . data ] });
break ;
case 'remove' :
api . applyTransaction ({ remove: [ update . data ] });
break ;
}
};
Optimistic Updates
async function updateRow ( rowId : string , changes : Partial < RowData >) {
const rowNode = api . getRowNode ( rowId );
const oldData = { ... rowNode . data };
// Optimistically update UI
api . applyTransaction ({
update: [{ ... rowNode . data , ... changes }]
});
try {
// Send to server
await fetch ( `/api/rows/ ${ rowId } ` , {
method: 'PATCH' ,
body: JSON . stringify ( changes )
});
} catch ( error ) {
// Revert on error
api . applyTransaction ({
update: [ oldData ]
});
console . error ( 'Update failed:' , error );
}
}
let nextPage = 0 ;
const pageSize = 100 ;
const gridOptions : GridOptions = {
rowData: [],
onBodyScrollEnd : () => {
const rowCount = api . getDisplayedRowCount ();
const lastIndex = api . getLastDisplayedRowIndex ();
// Load more when near the end
if ( lastIndex >= rowCount - 10 ) {
loadNextPage ();
}
}
};
function loadNextPage () {
fetch ( `/api/data?page= ${ nextPage } &size= ${ pageSize } ` )
. then ( response => response . json ())
. then ( data => {
api . applyTransaction ({ add: data });
nextPage ++ ;
});
}
Use Transactions
// ❌ Bad: Full replacement on every change
setInterval (() => {
const newData = fetchLatestData ();
api . setGridOption ( 'rowData' , newData );
}, 1000 );
// ✅ Good: Update only what changed
setInterval (() => {
const changes = fetchChanges ();
api . applyTransaction ({
update: changes . updated ,
add: changes . added ,
remove: changes . removed
});
}, 1000 );
Provide Row IDs
// ❌ Bad: No row IDs
const gridOptions = {
rowData: data
// Grid can't efficiently track changes
};
// ✅ Good: Provide stable IDs
const gridOptions = {
rowData: data ,
getRowId : ( params ) => params . data . id
};
Async Transactions for High Frequency
// High-frequency updates (100+ per second)
function handleDataStream ( updates : any []) {
updates . forEach ( update => {
api . applyTransactionAsync ({ update: [ update ] });
});
// Batch is automatically flushed
}
Transaction Result
Transactions return information about what was affected:
const result = api . applyTransaction ({
add: [ newRow ],
update: [ updatedRow ],
remove: [ oldRow ]
});
if ( result ) {
console . log ( 'Added:' , result . add ); // Array of added RowNodes
console . log ( 'Updated:' , result . update ); // Array of updated RowNodes
console . log ( 'Removed:' , result . remove ); // Array of removed RowNodes
}
Best Practices
Always provide getRowId - Essential for efficient updates and transactions
Use transactions - More efficient than replacing entire datasets
Batch updates - Combine multiple changes into a single transaction
Consider async transactions - For high-frequency updates (100+ per second)
Keep data immutable - Create new objects when updating, don’t mutate
Type your data - Use TypeScript generics for type safety