Editable dataframes are here! ✍️

Take interactivity to the next level with st.experimental_data_editor

Posted in Announcement, February 28 2023

Working with dataframes is at the heart of data science. If you’re like us, you probably load data from a CSV or a database, transform it in Pandas, fix it, transform it again, fix it again…and so on, until you’re happy. st.dataframe lets you visualize data instantly, but sometimes it’s not enough. You want to interact with it, not just look at a table!

Hence, we plan to release some major improvements for working with dataframes in the next few months. Today, we’re excited to launch…

Editable dataframes! 🎉

How to use it

Editable dataframes are supported via a new command, st.experimental_data_editor. It shows the dataframe in a table, similar to st.dataframe. But in contrast to st.dataframe, this table isn’t static. The user can click on cells and edit them. The edited data is then returned on the Python side.

Here’s an example:

edited_df = st.experimental_data_editor(df)
favorite_command = edited_df.loc[edited_df["rating"].idxmax()]["command"]
st.markdown(f"Your favorite command is **{favorite_command}** 🎈")

Try it out by double-clicking on a cell. 👆


This feature is experimental, i.e. it might change at any time. We plan to de-experimentalize it in the next few months. See here for details.

Advanced features

  • Adding and deleting rows. Just set the parameter num_rows=”dynamic”, and users can add rows to the table or delete existing rows:

  • Copy-and-paste support that’s compatible with Google Sheets, Excel, and others:

  • Bulk-editing by dragging the handle on a cell (similar to Excel):

  • Easy access to edited data. No need to compare the old and new dataframe to get the difference. Just use st.experimental_data_editor together with session state to access all edits, additions, and deletions.
  • Support for additional data types. Let your users edit lists, tuples, sets, dictionaries, NumPy arrays, or Snowpark and PySpark dataframes. Most types are returned in their original format.
  • Automatic input validation, e.g., number cells don’t allow characters.
  • Rich editing experience, e.g., checkboxes for booleans and dropdowns for categorical data. The date picker for datetime cells is coming soon!

New docs page

To support this release, we created a brand-new docs page on dataframes. It explains everything you need to know about st.dataframe and the new st.experimental_data_editor, including all of the sweet features you saw above. The best part is, it comes with lots of interactive examples! 🕹️

And of course, we also added st.experimental_data_editor to our API reference. Check out all of its parameters here.


Excited to jump in? Check out our demo app. It shows examples of using the data editor in practice. From guessing idioms to matrix operations over custom convolution filters, you can do a lot with this new feature.

Next up

Editable dataframes are only the beginning! 🌱

We have a bunch of new features for st.dataframe and st.experimental_data_editor in the pipeline for the next few months: showing images, clickable URLs in tables, letting the users select rows, and more. You can always follow our progress on roadmap.streamlit.app!

We’re excited to see what you build. Let us know your feedback in the comments below.

Happy Streamlit-ing! 🎈

This is a companion discussion topic for the original entry at https://blog.streamlit.io/editable-dataframes-are-here/

This is great!
I currently use Aggrid as a way of enabling complex user selection of rows from a database that are then processed further based on the user selection.

What would be great for the experimental data editor/dataframe would be:

a) pagination
b) filtering ability of each row

Thanks v much!


This is very cool. You guys are awesome.

Keep up the good work!!!


100%! Both planned. We don’t have a concrete timeline yet but we definitely want to do both of these things.

1 Like

Is it possible to get the selected row as either return value or state? I’m looking forward to ditching aggrid

Row selections is one of the next features we will release for dataframes in the next couple of weeks/months :tada:


I ran the example code and got an error like this:
TypeError: data type ‘complex256’ not understood

This problem has occasionally occurred when using st.dataframe before

That’s awesome!
Please add the Context Menu to the list as well!


We already have a fix ready for this that will be released in a few days with version 1.20.


Thank you guys for this great new feature!

I am wondering how to implement an editable dropdown. I have seen it in the demo but cannot find any explanation how to do it. I tried it with a list in the dataframe but this does not work.

Any help on that is much appreciated :slight_smile:

1 Like

Dropdowns are automatically used for categorical columns.

import pandas as pd
import streamlit as st

data = pd.Series(["A", "B", "C"])

This is really lovely, thank you!!!

I get stuck in the situation where I need to edit a value twice to get it to “stick”. This is because I want to save my updated dataframe in session state. The usual solution to that issue is found here and here.

However, the session_state[‘key’] of experimental_data_editor returns a dict and not the updated dataframe. I see the point in just returning the changed values, but I would really need a dataframe to be able to take it with me to other pages in my multipage app. So if you could fix this that would make me even happier.

You could perhaps just extend the dict to contain the whole new dataframe. For example session_state[‘key’].data could return dataframe (yes I know this is also ag-grid’s naming convention but it’s quite standard :frowning: ). .edited_cells, .added_rows and .deleted_rows could stay, I think it’s well thought through to have them as well.

Again, many thanks!


This is dope. I have been working on an app and using ag grid to do crazy stuff with my excel file (a nightmare).

With this, i can now replace dozens of lines of code with one single command.

Like it, thanks guys !


Great feature. thanks for adding this feature.
How does one validate the data? If a user types an invalid value, I want to throw a message that its invalid and then revert back to the previous state.

def validate_cnt():
    if 'dept_tbl' in st.session_state:
        val = st.session_state.dept_tbl['edited_cells']
        for k, v in val.items():
            if v > 100:

data = {
    'Department': ['Department 1', 'Department 2', 'Department 3', 'Department 4', 'Department 5'],
    'Number': [10, 20, 30, 40, 50]
df = pd.DataFrame(data)
st.experimental_data_editor(df, key="dept_tbl",                                    

If someone edits 30 to 300, I wanted to fail them as the number should be < 100
How does one achieve it? I tried using on_change to validate, but wasn’t able to revert the number.
Example, user updated 200 instead of 40 for value 20, how do we revert it to 20 when update fails?


Great feature. Just wondering, is there way to add Sparkline column to the Grid? The thumbnail of Data editor in API documentation shows the grid with Sparkline.

This is awesome. I’m curious, are people persisting the edited data somewhere? How would you show the changes to another user or “save” the changes (other than saving them to a database somehow)?

I’m persisting the edited data to database.
You can also use session_state to store this data.
However, I have other challenges, such as:

  • how do you disable a column from being edited
  • Based on a column edit, how does values reflect in other column (ex: colA value is 20, based on colA, I want to show a value in colB as “Positive”).
  • How do you validate the data when a user is entering the data…

I wonder about the same thing. I would like to allow the users to edit values in certain columns, and other columns in the same dataframe would be recalculated and updated.

From the implementation of the editor so far, I don’t think this is possible?

Edit: here’s my attempt to solve the scenario above. It is obviously clumsy. Any feedback would be most welcome. Thanks.

import pandas as pd
import streamlit as st

if 'df_base' not in st.session_state:
    st.session_state.df_base = pd.DataFrame({'a': [1, 2, 3], 'b': [11, 22, 33], 'c': ['x', 'y', 'z']})

df = st.session_state.df_base

st.markdown('Column "b" is editable, and will affect column "d" = "a" + "b"')

c1, c2, c3 = st.columns(3, gap="small")
with c1:
    df_left = df['a']

with c2:
    editable_column = 'b'
    df_edit = df[[editable_column]]
    edited_df = st.experimental_data_editor(df_edit)

with c3:
    df_right = df[['c']]
    df_right['d'] = df['a'] + edited_df['b']

            '* hide the indices of the edit & right dfs\n'
            '* "squeeze" the dfs together')


Thanks. This is certainly a quick and dirty solution. But I believe, we shouldn’t be doing all this. There should be a clean way for us to make columns non-editable.

To extend your solution, here is the last step:

df_join = pd.merge(df_left, df_right, left_index=True, right_index=True)

I have raised a issue to restrict columns in data_editor.
Please feel free to raise the priority of the issue: