# Create

To create a grid with ipyaggrid you need:

  • bring you data as (1) a list of dict or (2) a dataframe
  • define ag-Grid gridOptions as a dict - see ag-Grid doc
  • pass them to the Grid constructor, along with specific options - see below
  • display the ipywidget thus created


At any moment, if you want to know how to create an ipyaggrid like the doc examples, search the example you need in this interactive notebook:


# Sample Setup

The params of the Grid constructor must have the following types:

# Widget options
width : int or str,
height : int,
center : bool,
theme : str,

# Grid setup
grid_data : list or pandas dataframe,
grid_options : dict,
grid_options_multi : list of dict,
columns_fit : str,
index : bool,
keep_multiindex : bool,
compress_data : bool,

# Menu options
quick_filter : bool,
export_csv : bool,
export_excel : bool,
show_toggle_delete : bool,
show_toggle_edit : bool,
paste_from_excel : bool,
export_mode : str,
export_to_df : bool,
hide_grid : bool,

# Customization options
menu: dict,
css_rules : str,
js_helpers_custom : str,
js_pre_helpers : list of str,
js_pre_grid : list of str,
js_post_grid list of str,
user_params: dict,

license : str

The default parameters are the following:

def __init__(self,





# User Data

grid_data: list or pandas.DataFrame, default=[]

The data can be input as (1) a list of dict, each a row, or (2) a dataframe.


The dataframe must have all column names set.
Columns without name are ignored.

If data is input as a dataframe it is translated to ag-Grid format.

In most of the examples below we use a simple dataframe df.

<class 'pandas.core.frame.DataFrame'>
MultiIndex: 64 entries, (A0, B0, C0, D0) to (A3, B1, C3, D1)
Data columns (total 9 columns):
(a, bar, zap)    64 non-null float64
(a, bar, zip)    64 non-null float64
(a, foo, zap)    64 non-null float64
(a, foo, zip)    64 non-null float64
(b, bah, zap)    64 non-null float64
(b, bah, zep)    64 non-null float64
(b, bah, zyp)    64 non-null float64
(b, foo, zap)    64 non-null float64
(b, foo, zep)    64 non-null float64
dtypes: float64(9)
memory usage: 5.1+ KB

You can also use a list of rows as follows. However it is probably a lot more convenient to input a pandas dataframe, the de facto standard Python container for tabular data.


The Python-JavaScript conversion of list/dict-array/object is handled by the ipywidgets library.

cars = [
    {'carName': 'Chevelle', 'origin': 'US', 'make': 'Chevrolet', 'price': 30415},
    {'carName': 'Skylark 320', 'origin': 'US', 'make': 'Buick', 'price': 21042},
    {'carName': 'PL411', 'origin': 'Asia', 'make': 'Datsun', 'price': 27676}

# Grid Options

grid_options: dict, default={}

The grid options are exactly those of ag-Grid, in full transparency. Basically, it should contain column definitions, and some configuration for the grid. See the official documentation to see the possibilities.

The ag-Grid API is very rich and explicit and well documented, and their website has tons of examples that enable you to easily play with all the features. We can only recommend to use the plunkers they provide to fine tune the right configuration for your use case.

Here is a sample basic grid:

column_defs = [{'headerName':'Continent','field':'continent','rowGroup':True, 'hide':True},
               {'headerName':'Country','field':'country','rowGroup':True, 'hide':True},
                   if (params.value !== undefined && params.value !== null){
                   return (params.value.substring(0,4));
                   return "" 
               {'headerName':'Mass','field':'mass', 'aggFunc':'avg'},

grid_options = {
    'columnDefs' : column_defs,
    'enableSorting': True,
    'enableFilter': True,
    'enableColResize': True,
    'enableRangeSelection': True,

g = Grid(grid_data=dfm,

And the output:

The organisation of the Grid is simple:

  • All convenience buttons and filters and menus are located in top part
  • The grid produced by ag-Grid is below

# Parameters

This section describes the ipyaggrid basic configuration parameters - as opposed to the advanced customization parameters which often require JavaScript and CSS.

# Dimensions and placement

width: int or string, default='100%'
height: int, default=350 center: bool, default=False

The size of the grid is determined by the width and height parameters.
Width can either be a number of pixels or a string like 80% or 550px Otherwise the width will automatically be set to 100%, and height to 350px.

The center param sets margin to auto for the div containing the grid and the menu. It should be helpful if you wish to export the grid in other contexts than the notebook.

column_defs = [{'field': c} for c in dfm.columns[:3]]

grid_options = {
    'columnDefs' : column_defs,

g = Grid(grid_data=dfm,

# Columns Size

columns_fit: str, default='size_to_fit'

There are 3 possible values:

  • size_to_fit: fills the grid space using all the columns. This is useful if you have few columns and you want the grid to look nice
  • auto: uses the minimum space required by all the visible elements of a column. This may be useful if you have several columns and you don't want them to appear too big, but careful to the columns "out of the box" that may remain too big/small.
  • <empty string>: allows you to set the size of each column in the columnsDefs yourself

Side Note

  • you can manually auto-size one column by double-clicking on the resizing icon a the top-right of a column
  • wrong auto-sizing on hidden columns is due to columns virtualisation. If you wish to auto-size all columns, you should perhaps use 'suppressColumnVirtualisation' = True in the gridOptions.

Below is an example of an "auto-sized" grid.

column_defs = [{'field': c, 'width':500} for c in dfm.columns]

grid_options = {
    'columnDefs' : column_defs,

g = Grid(grid_data=dfm,

# Themes

theme: str, default='ag-theme-fresh'

ag-Grid comes with several themes that you can choose from. ag-theme-fresh is the default theme, and ag-theme-balham is definitely one of the nicest (enterprise feature).

The available themes are presented in themes documentation page

In addition to the official themes ipyaggrid contains a custom theme ag-theme-excel for an Excel like rendering.

column_defs = [{'field': c} for c in dfm.columns[:7]]

grid_options = {
    'columnDefs' : column_defs,

themes = [
    'ag-theme-excel', # custom style added by ipyaggrid

g = Grid(grid_data=dfm,
#          theme='ag-theme-balham',
#          theme='ag-theme-excel',

# Save as File

export_csv: bool, default=False
export_excel: bool, default=False

ag-Grid enables exporting as CSV or Excel (enterprise feature).
ipyaggrid makes these options available through a button above the grid.

column_defs = [{'field': c} for c in dfm.columns[:7]]

grid_options = {
    'columnDefs' : column_defs,

g = Grid(grid_data=dfm,

# Paste from Excel

paste_from_excel: bool, default=False

Exporting to Excel can be useful, but you might also want to paste data and create columns directly from Excel, without creating a dataframe or parsing a json in python.

Note that the grid must have at least one cell you can click to be able to paste. To do so, follow the syntax of the example.


By inputing the data this way, you won't be able to define all columnDefs very precisely. You can enable rowGroup by using the defaultColDef in the gridOptions. However, it will not be possible to disable it on some columns.

In general if you want a very specific design, you should always input your data using a dataframe as it is possible to read an Excel file with pandas.

It is not possible to paste from the context menu in a browser, unless the user chooses to enable it (for more details see ag-Grid documentation).

grid_options = {

g = Grid(grid_options=grid_options,

Try to input a selection of the grid you exported in the example above.

# QuickFilter

quick_filter: bool, default=False

This ag-Grid feature is made available via a filter field above the grid.
Filtering takes place after each keystroke which makes it a very convenient basic search. For more advanced filtering use the filtering by column (available through columnDefs).

column_defs = [{'field':'origin', 'headerName': 'Origin'},
                  {'field':'make', 'headerName': 'Make'},
                  {'field':'carName', 'headerName':'Model'},
                  {'field':'price', 'headerName': 'Price'}]

grid_options = {
    'columnDefs' : column_defs,

g = Grid(grid_data=cars,

# License

license: str, default=''

To use enterprise features you need an ag-Grid license.
You can either:

  • paste your license as a string, or
  • use the get_license function which helps you get it from a file in you home directory

Example: get_license(filename='.ag_grid_license').
Then use it in Grid(license=get_license()).

# Index

index: bool, default=False

Automatically adds your dataframe index as the first column.
Otherwise the dataframe index is dropped.

column_defs = [{'field': dfm.index.name}] + [{'field': c} for c in dfm.columns[:3]]

grid_options = {
    'columnDefs' : column_defs,

g = Grid(grid_data=dfm,

# Multi Index

keep_multiindex: bool, default=True

In case of multiindex dataframe:

  • convert column multiindex into ag-Grid multi level headers
  • convert row multiindex into grouped left most columns

This feature enables to display multiindex dataframes as grid without the user having to reformat for ag-Grid, saving them a tedious exercise.

For this example we are going to work with a complex multi index dataframe, with four levels on rows and 3 levels on columns. Here is the representation of the dataframe, and the result in the ipyaggrid. keep_multiindex works on columns and rows.

column_defs = []

grid_options = {
    'columnDefs' : column_defs,

g = Grid(grid_data=df,

# Multi Options

grid_options_multi: list, default=[]

You can also pass several grid options to be applied to the same data.
The benefit are:

  • compact representation: A dropdown menu allows to switch between grid options
  • compact output: Data - often the large part - is not duplicated
css_rules = """
.number-cell {
    text-align: left;
.price-high {
    color: red;


ccf = "function(params) { return params.value >32000 ? 'price-high' : ''; }"
column_defs_1 = [{'field':'origin', 'headerName': 'Origin'},
                  {'field':'make', 'headerName': 'Make'},
                  {'field':'carName', 'headerName':'Model'},
                  {'field':'price', 'headerName': 'Price', 'cellClass': ccf}]

column_defs_2 = [{'field':'origin', 'headerName': 'Origin', 'hide':True, 'rowGroup':True},
                  {'field':'make', 'headerName': 'Make'},
                  {'field':'carName', 'headerName':'Model'},
                  {'field':'price', 'headerName': 'Price'}]

grid_options_1 = {
    'columnDefs' : column_defs_1,
    'enableSorting': True,
    'enableFilter': True,
    'enableColResize': False,

grid_options_2 = {
    'columnDefs' : column_defs_2,
    'enableSorting': False,
    'enableFilter': False,
    'enableColResize': True,
    'rowSelection': 'multiple',

g = Grid(grid_data=cars,
            grid_options_multi=[('Sorting, color, filter', grid_options_1),
                                ('Selection, grouping, no-filter', grid_options_2)],

# Hide Grid

hide_grid: bool, default=False

You may want to hide completely the grid and make available only the export buttons. In this very special case, this option is for you.

# Compress Data

compress_data: bool, default=True

In the communication between Python and JavaScript, you may use very large sets of data with thousands of lines. ag-Grid can handle it easily, but the transfer between Python and JavaScript can be slow, so we need be able to compress (i.e. gunzip) the data in transit if the dataset is large.
To disable this option set compress_data to False.

# Disable Sync

unsync: bool, default=False

Disables the changes in grid_data_out when set to True. This is not an input param. The correct way to use it is to disable it after grid creation with grid.unsync = True.

# Export to Python ⭐️

export_mode: string, default='disabled'
export_to_df: bool, default=True

A key feature of ipywidgets is the ability to sync data back from the JavaScript frontend, typically after user manipulation, to the Python kernel in the variable grid_data_out.

The variable grid_data_out is dict with the following keys, if present:

  • grid: all grid data
  • rows: all selected rows data at export time
  • cols: all selected cols data at export time
  • range: all selected range data at export time

Not all keys are always present, only those which have been synced.

In ipyaggrid the syncing back to Python is controlled by the variable export_mode.
It can take 3 values:

  • disabled: disables all data exporting from JavaScript to Python. You can still manipulate data from Python code.
  • buttons: creates several buttons in the menu depending on the selection mode you chose in the grid_options:
    • for rowSelection, one button to update key rows with selected data
    • for rangeSelection, three buttons to update keys rows, cols and range with selected data
  • auto: identical to button mode except grid_data_out is continuously updated as your selection changes.

The output type is either a pandas dataframe or a list of Python dict, depending on the value of export_to_df.

It can be changed after instantiation : grid.export_to_df=False

grid = Grid(grid_data=df,

To experiment with the various behaviors, play with this mybinder live notebook:


As auto updates all dimensions at once, you should not use it in rangeSelection mode when working with large datasets. It may significantly slow down your browser and kernel. Intead prefer button mode or auto mode with rowSelection.

Moreover, you should not export data in rangeSelection when in pivot/grouping mode. The result would make little sense.

Instead, always prefer to use filtering/removing columns and exporting the grid.

# Export grid and aggregation

You may have noticed a counter next to the 'Export Grid' button. It allows the user to choose the right aggregation level.

Take the following grid :

You could choose to keep the values of the countries or the sports for example. The 0 aggregation level is always the less expanded one. If you expand it once, and you want to keep that data.

Here are the respective dataframe outputs for 0 and 1.

# Delete data

show_toggle_delete: bool, default=False

After a grid is displayed you may want to delete some lines on a key press, from Python or a keyboard shortcut.

If show_toggle_delete is set to True, the user gets a checkbox that he can tick and then delete a selection. The checkbox is there to avoid manipulation error.

column_defs = [{'field': c} for c in dfm.columns[:7]]

grid_options = {
    'columnDefs' : column_defs,
    'rowSelection': 'multiple',

g = Grid(grid_data=dfm,

Deleting data also works in rangeSelection mode (enterprise feature!).

Finally, you can use a Python function delete_selected_rows to do the same, irrespective of the checkbox presence and value.

For a live demo see this MyBinder section.

# Edit data

show_toggle_edit: bool, default=False

If you want to edit the data, enable show_toggle_edit, this will create a checkbox. Please tick it and then edit the data by double clicking a cell. The checkbox is a security against manipulation errors.

column_defs = [{'field': c} for c in dfm.columns[:11]]

grid_options = {
    'columnDefs' : column_defs,

g = Grid(grid_data=dfm,

For a live demo see this MyBinder section.

# Sync on Edit

sync_on_edit: bool, default=False

If you want to automatically sync the grid after edit, set sync_on_edit=True. This will instantly update .grid_data_out['grid'] with the updated dataframe without the need to use the .get_grid() method. Avoid if the dataframe is very large.

df2 = pd.DataFrame({'val':['a']})
column_defs = [{'headerName': 'Col', 'field': 'val', 'editable': True}]

grid_options = {
    'columnDefs': column_defs

g = Grid(grid_data=df2,


# upon edit the updated dataframe is: g.grid_data_out['grid']

# Sync Grid

sync_grid: bool, default=True

If you want to automatically sync the whole grid after initialization, grid data update, set sync_grid=True. This will automatically update .grid_data_out['grid'] with the updated dataframe without the need to use the .get_grid() method. Avoid if the dataframe is very large.

df2 = pd.DataFrame({'val':['a']})
column_defs = [{'headerName': 'Col', 'field': 'val', 'editable': True}]

grid_options = {
    'columnDefs': column_defs

g = Grid(grid_data=df2,


# upon initialization or grid data update the dataframe is: g.grid_data_out['grid']

# Update Data

You can also update the grid bulk data under the constraint that the options and the column names remain unchanged. To do so use the grid.update_grid_data(new_data) function.

For a live demo see this MyBinder section.

# Live Demo

# Play with Data

In addition to manipulating the grid data using the frontend interface, a user can also perform some operations from Python:

  • .export_grid(): export all data
  • .export_selected_rows(): export only selected rows
  • .export_selected_columns(): export only selected columns
  • .update_grid_data(data): replace data in existing grid
  • .delete_selected_rows(): delete only selected rows

# MyBinder

The following live notebook on mybinder contains the examples of the previous sections:

Last Updated: 11/23/2023, 10:50:09 AM