How to dynamically add a row to an ag grid (ideally at the top)

I want to click an ‘add row’ button and dynamically add a row to an AG Grid.

Ideally, I would like the new row to be at the top so it’s obvious to the user.

I don’t see any examples of this in the documentation. Wondering if there’s a simple way to accomplish this in the AG Grid.


Hi @zack_nc !

You can use the rowTransaction parameter to update data, add/remove rows.
You can find some details here:

In your case, if you prefer to add one row at the top, you can use add with addIndex=0, like in the example below:

    "addIndex": 0,
    "add": [{"make": None, "model": None, "price": None}]

There is no example using addIndex in Dash docs (yet), but you can check:

Here is a simple example:

import dash_ag_grid as dag
from dash import Dash, html, Input, Output, callback

app = Dash(__name__)

rowData = [
    {"make": "Toyota", "model": "Celica", "price": 35000},
    {"make": "Ford", "model": "Mondeo", "price": 32000},
    {"make": "Porsche", "model": "Boxster", "price": 72000},

columnDefs = [{"field": "make"}, {"field": "model"}, {"field": "price"}]

app.layout = html.Div([
    html.Button("Add Row", id="transactions-add"),
        defaultColDef={"editable": True, "resizable": True},

    Output("my-grid", "rowTransaction"),
    Input("transactions-add", "n_clicks"),
def update_rowdata(_):
    return {
        "addIndex": 0,
        "add": [{"make": None, "model": None, "price": None}]

if __name__ == "__main__":

1 Like

Thanks! This is perfect.

Related question: is there a similar pattern for a delete button? As of now, I can get the callback and delete the row from the DB, and then re-load the table. This “works”, but the table will always scroll to the top because of the reload.
Is there a way I can simply remove the row in place?

Hello @zack_nc,

Yes. The rowTransaction can do add, update and delete.

You just pass the data as an array to the dictionary with delete as the key. If it matches the grid row exactly, it will delete.

If you use getRowId, it will remove the one that matches the key. So even if the row has been altered, it will still be remove.


I think I understand your instructions, but seem to be doing something wrong.

My selectedRows array contains the single row I want to delete, but it doesn’t disappear when this code runs.

And I’m not super sure what you mean by"if you use getRowId"
Is that a property I can pass to the return object?

Output(‘ftrom-table-awep-parent’, ‘rowTransaction’),
[Input(‘ftrom-add-mapping-row’, ‘n_clicks’),
Input(‘ftrom-delete-mapping-row’, ‘n_clicks’)],
[State(‘ftrom-table-awep-parent’, ‘cellClicked’),
State(‘ftrom-table-awep-parent’, ‘selectedRows’)])

def rowHandler(n_clicks1, n_clicks2, cellClicked, selectedRows):

if dash.callback_context.triggered_id == 'ftrom-add-mapping-row':
    return {
        "addIndex": 0,
        "add": [{"parentName": 'TODO: New Parent Name', "isCompetitor": False, "mapped_count": None}]

elif dash.callback_context.triggered_id == 'ftrom-delete-mapping-row':
    if selectedRows is not None and len(selectedRows) > 0:
        return {
            "delete": selectedRows
        raise PreventUpdate
    raise PreventUpdate

If you want to delete the selected rows, then just pass True to deleteSelectedRows

Thank you but I don’t understand.

Is that different from using the rowTransaction?

Yes, rowTransaction and deleteSelectedRows are different.

My apologies, the key for the dictionary shouldn’t be delete, it should be remove.


“remove” also does not appear to work.

Can you please point me at an example for deleteSelectedRows?

Sure, here you go:

import dash_ag_grid as dag
from dash import Dash, html, dcc, Input, Output, callback, no_update
import pandas as pd

app = Dash(__name__)

df = pd.read_csv(

columnDefs = [
    {"field": "athlete"},
    {"field": "age"},
    {"field": "country"},
    {"field": "year"},
    {"field": "date"},
    {"field": "sport"},
    {"field": "gold"},
    {"field": "silver"},
    {"field": "bronze"},
    {"field": "total"},

app.layout = html.Div(
            "This grid has multi-select rows.  Use shift click or ctr click to select rows"
        html.Button(id='deleteRows', n_clicks=0, children="Delete Selection"),
            defaultColDef={"resizable": True, "sortable": True, "filter": True, "minWidth": 125,},
    style={"margin": 20},

    Output("selections-multiple-output", "children"),
    Input("selection-multiple-grid", "selectedRows"),
def selected(selected):
    if selected:
        selected_athlete = [s["athlete"] for s in selected]
        return f"You selected athletes: {selected_athlete}"
    return "No selections"

    Output('selection-multiple-grid', 'deleteSelectedRows'),
    Input('deleteRows', 'n_clicks')
def deleteRows(n):
    if n:
        return True
    return no_update

if __name__ == "__main__":