This blog demonstrates how to add rows in AG Grid using a Pinned Row and the Transaction API, avoiding the need for a separate form UI.
You will learn how to:
- Implement and style an empty pinned row.
- Listen for and process cell edits within the pinned row.
- Insert a new row using the data entered by the user.
- Flash the new row to draw the user’s attention.
Example & Source Code
In the example below, entering data into the pinned top row triggers automatic addition of a new row, highlighted by a green flash as visual confirmation.

TL;DR
A quick summary of this how-to
Configure Grid Options
const [pinnedRowData, setPinnedRowData] = useState<>({});
<AgGridReact<AthleteData>
...
// Pin an empty row at the top for data entry
pinnedTopRowData={[pinnedRowData]}
// Listen for pinned row edits
onCellEditingStopped={onCellEditingStopped}
/>
Handle Pinned Row Cell Edits
// Checks if all required fields in the pinned row are filled
const isPinnedRowComplete = () => {
return columnDefs.every((colDef) => {
if (!colDef.field) return false;
const v = pinnedRowData[colDef.field!];
return v != null && v !== '';
});
};
const onCellEditingStopped = useCallback((params: CellEditingStoppedEvent) => {
// Only process pinned row edits
if (params.rowPinned !== 'top') return;
// Check all pinned row cells have a value
if (isPinnedRowComplete()) {
addNewRow();
}
}, [pinnedRowData]);
Add a New Row
const addNewRow = () => {
// Add the new row to the grid data
const transaction = gridRef.current?.api.applyTransaction({
add: [pinnedRowData],
});
// Reset the input row for next entry
setPinnedRowData({});
gridRef.current?.api.setGridOption('pinnedTopRowData', [pinnedRowData]);
// Flash the newly added row to draw attention
// Note: add delay to ensure transaction & updates complete
setTimeout(() => {
gridRef.current?.api.flashCells({
rowNodes: transaction?.add,
});
}, 100);
};
Configuring the Data Grid
There are a few Grid Options required for this demo, including:
pinnedRowTopData
- Initialises pinned row for user data entry. The data will be automatically updated by the grid whenever a user edits the value of a cell.rowNumbers
- Enables the row number column. ThevalueFormatter
disables this for pinned rows.onCellEditingStopped
- Defines a callback function that is called whenever a user has stopped editing a cell.
// Show row numbers for non-pinned rows
const rowNumbersOptions = {
valueFormatter: (params: ValueFormatterParams) => {
return params?.node?.rowPinned ? '' : params?.value;
},
};
<AgGridReact
...Other options, including rowData, colDefs, etc...
// Pin an empty row at the top for data entry
pinnedTopRowData={[pinnedRowData]}
// Show row numbers for non-pinned rows
rowNumbers={rowNumbersOptions}
// Listen for pinned row edits
onCellEditingStopped={onCellEditingStopped}
/>
Styling the Pinned Row
All pinned rows have the ag-row-pinned
CSS class applied to them, so to style the pinned top row, override the CSS class in the application styles:
/* Pinned input row */
.ag-row-pinned {
background-color: #f8f9fa;
font-style: italic;
color: #6c757d;
border-bottom: 2px solid #e9ecef;
}
.ag-row-pinned:hover {
cursor: text;
}
Styling Pinned Row Cell Edits
cellClassRules
allow you to apply custom CSS classes based on the value of a cell. To apply a class whenever the user enters a value into a pinned row cell, provide a cellClassRules
object that returns the pinned-cell-editing
class when the node is rowPinned
and has a value
.
const cellClassRules = {
'pinned-cell-editing': (params: CellClassParams) => params?.node?.rowPinned && params.value,
};
const defaultColDef: ColDef = {
...
cellClassRules,
};
The pinned-cell-editing
class in this demo adds an orange border to the left of the cell and slightly changes the text and background:
/* Cells with values in pinned row */
.pinned-cell-editing {
font-style: normal;
color: #333;
font-weight: 500;
background-color: #fff3cd;
border-left: 2px solid #fd7e14;
}
/* Hover effect for empty cells */
.pinned-row .ag-cell:not(.pinned-cell-editing):hover {
background-color: #f1f3f4;
}
Handling Pinned Row Cell Edits
In the onCellEditingStopped
callback, we need to check that every pinned row cell has a value. We do this in the isPinnedRowComplete()
function by looping through each field in our column definition and then looking up the pinnedRowData
for that column. If all columns have a value, we can add the new row; otherwise, we can ignore the edit event:
const isPinnedRowComplete = () => {
return columnDefs.every((colDef) => {
if (!colDef.field) return false;
const v = pinnedRowData[colDef.field!];
return v != null && v !== '';
});
};
const onCellEditingStopped = useCallback((params: CellEditingStoppedEvent) => {
// Only process pinned row edits
if (params.rowPinned !== 'top') return;
// Check all pinned row cells have a value
if (isPinnedRowComplete()) {
addNewRow();
}
}, [pinnedRowData]);
Updating the Data Grid
There are a few steps to take when adding a new row:
Adding a new row
Firstly, we can use the applyTransaction
API to add the pinnedRowData
to the grid by supplying this via the add
property. There is an optional addIndex
property to specify where to add the row, which defaults to the end of the data.
Clearing the pinned row
Next, we need to clear the data from the pinned row:
- First, update the state of
pinnedRowData
to an empty object. - Then use the
setGridOption('pinnedTopRowData', [pinnedRowData])
API to update the pinned row, ready for the user to add more data.
Flashing new row
Finally, we can use the flashCells
API to flash the newly added row. We provide the result from the transaction to ensure the flash only occurs after a row has been added successfully. This is wrapped in a setTimeout
to ensure the transaction is complete, along with any model updates (e.g. sorting, filtering, etc...).
const addNewRow = useCallback(() => {
// Add the new row to the grid data
const transaction = gridRef.current?.api.applyTransaction({
add: [pinnedRowData],
});
// Reset the input row for next entry
setPinnedRowData({});
gridRef.current?.api.setGridOption('pinnedTopRowData', [pinnedRowData]);
// Flash the newly added row to draw attention
// Note: add delay to ensure transaction & updates complete
setTimeout(() => {
gridRef.current?.api.flashCells({
rowNodes: transaction?.add,
});
}, 100);
}, [pinnedRowData]);
Conclusion
This blog shows developers how to add rows in AG Grid by using a pinned top row and the Transaction API. It covers configuring an editable pinned row, capturing and validating user input, programmatically adding new rows, and visually highlighting inserted data. Code examples for React, Angular, Vue, and plain JavaScript are included to illustrate the approach clearly.
Learn more about the features used in this blog on our docs:
Next Steps
New to AG Grid? Get started in minutes, for free:
Considering AG Grid Enterprise? Request a free two-week trial licence to test your application in production and get direct access to our support team.
Happy coding!