Skip to content
Topics
Streamlit
What Is a Streamlit DataFrame? How to Use st.dataframe and st.data_editor

What Is a Streamlit DataFrame? How to Use st.dataframe and st.data_editor

Updated on

A Streamlit dataframe is the main way to show tabular data inside a Streamlit app.

What a Streamlit dataframe means

When people say "Streamlit dataframe," they usually mean one of two things:

  • st.dataframe for displaying data interactively
  • st.data_editor for letting users edit that data in the app

In plain terms, Streamlit takes a table-like object such as a pandas DataFrame, a Polars DataFrame, or another dataframe-like structure and turns it into a browser-friendly table. That table can be readable, scrollable, selectable, and sometimes editable depending on which API you use.

Under the hood, Streamlit's interactive dataframe UI is built on Glide Data Grid (opens in a new tab), which is one reason it behaves more like a rich data grid than a plain HTML table.

Why Streamlit dataframes matter

For many Streamlit apps, the dataframe is not just one component among many. It is the product.

That is especially true when you are building:

  • internal analysis tools
  • lightweight review workflows
  • data cleaning utilities
  • monitoring dashboards
  • upload-and-preview apps

If the table feels clumsy, the whole app usually feels clumsy.

What st.dataframe and st.data_editor are actually for

The key decision is not "how do I show a table?" but "what kind of interaction should the table support?"

Use st.dataframe when the table is mainly for viewing, scanning, selecting, and exploring.

Use st.data_editor when the user should be able to change values, add rows, or remove rows.

After reading this guide, you should be able to:

  • choose the right table API for the job
  • configure columns more clearly
  • handle styling and editing without brittle hacks
  • avoid common performance and UX mistakes with large tables

Quick Answer

If the table should be read-only, start with st.dataframe.

If the table should be editable, start with st.data_editor.

That single distinction solves most beginner confusion around Streamlit tables.

Quick Start

This first example shows the simplest read-only table.

import pandas as pd
import streamlit as st
 
df = pd.DataFrame(
    {
        "product": ["A", "B", "C"],
        "revenue": [1250, 980, 1540],
    }
)
 
st.dataframe(df, width="stretch")

This second example shows the editable version of the same idea.

import pandas as pd
import streamlit as st
 
df = pd.DataFrame(
    {
        "product": ["A", "B", "C"],
        "approved": [True, False, True],
    }
)
 
edited_df = st.data_editor(df, width="stretch")
st.write(edited_df)

The difference is simple but important: st.dataframe displays data, while st.data_editor returns edited data back to your script.

What st.dataframe is good at

st.dataframe is best when users need to inspect data without changing it.

That usually means:

  • browsing uploaded data
  • checking filtered results
  • comparing records
  • selecting rows or cells for follow-up actions
  • reviewing metrics in a table before charting them

Modern st.dataframe also supports selection behavior through on_select and selection_mode, which makes it useful for lightweight table-driven workflows.

What st.data_editor is good at

st.data_editor is best when the table is part of the input flow.

That usually means:

  • fixing labels
  • marking approvals
  • editing planning tables
  • creating or deleting rows in a small operational dataset
  • letting users adjust parameters in a grid-like form

This is why st.data_editor often feels less like "a chart companion" and more like "a spreadsheet step inside the app."

Core arguments you should know

st.dataframe

ArgumentWhat it doesWhen to care
widthControls table widthUse width="stretch" for full-width layouts
heightControls table heightUseful for large tables
hide_indexHides index columnsGood for cleaner presentation
column_orderReorders or limits columnsUseful for tighter views
column_configFormats and customizes columnsImportant for production-like tables
on_selectEnables selection-driven reruns or callbacksGood for row-based workflows
selection_modeChooses row, column, or cell selectionUseful for interaction design
row_heightChanges row heightHelpful for dense or multi-line content
placeholderSets text for missing valuesUseful for cleaner display

st.data_editor

ArgumentWhat it doesWhen to care
width / heightControls editor sizeImportant in dense layouts
hide_indexHides index columnsGood for simpler editing views
column_configFormats and constrains columnsImportant for editing UX
num_rowsControls whether rows are fixed, addable, or deletableCritical when users should manage rows
disabledLocks all or some columnsUseful when only some fields should change
on_changeRuns a callback when edits happenUseful for stateful workflows
row_heightChanges row heightHelpful for custom cell presentation
placeholderSets text for missing valuesUseful for sparse data

Configure columns before you reach for CSS

For most table cleanup, column_config is the first tool to use.

This example shows how to rename and format columns without changing the underlying dataframe.

import pandas as pd
import streamlit as st
 
df = pd.DataFrame(
    {
        "item": ["A", "B", "C"],
        "price": [1.5, 2.0, 3.25],
        "margin": [0.12, 0.18, 0.27],
    }
)
 
st.dataframe(
    df,
    width="stretch",
    column_config={
        "price": st.column_config.NumberColumn("Price", format="$%.2f"),
        "margin": st.column_config.NumberColumn("Margin", format="%.0f%%"),
    },
)

This is usually better than custom CSS because it is clearer, safer, and aligned with the Streamlit API.

Style data carefully

If you want visual emphasis, a pandas Styler can still help.

This example highlights the maximum value in each numeric column.

import pandas as pd
import streamlit as st
 
df = pd.DataFrame(
    {
        "region": ["APAC", "EMEA", "LATAM"],
        "revenue": [120, 180, 90],
        "profit": [25, 41, 18],
    }
)
 
st.dataframe(df.style.highlight_max(axis=0), width="stretch")

The practical limit is important here: Streamlit supports useful styling such as custom cell values, colors, and font weights, but not every exotic pandas styling feature maps cleanly into the interactive table.

Use row selection for lightweight workflows

st.dataframe can now be more than a passive display widget. If you enable selection, it can behave more like an input.

This example shows row selection driving a follow-up detail view.

import pandas as pd
import streamlit as st
 
df = pd.DataFrame(
    {
        "ticket_id": [101, 102, 103],
        "status": ["Open", "In Review", "Closed"],
        "owner": ["Ava", "Ben", "Chen"],
    }
)
 
selection = st.dataframe(
    df,
    on_select="rerun",
    selection_mode="single-row",
    width="stretch",
)
 
rows = selection["selection"]["rows"]
if rows:
    selected_row = df.iloc[rows[0]]
    st.write("Selected ticket:", selected_row.to_dict())

This is often enough for simple review tools without building a heavier custom UI.

Use st.data_editor when editing is the product

The next example shows a grid where some columns are editable and some are intentionally locked.

import pandas as pd
import streamlit as st
 
df = pd.DataFrame(
    {
        "task": ["Check schema", "Approve upload", "Publish report"],
        "priority": [1, 2, 3],
        "done": [False, False, True],
    }
)
 
edited_df = st.data_editor(
    df,
    width="stretch",
    disabled=["task"],
    column_config={
        "priority": st.column_config.NumberColumn("Priority", min_value=1, max_value=5),
        "done": st.column_config.CheckboxColumn("Done"),
    },
)
 
st.write(edited_df)

This works well because the user knows exactly what is editable and what is not.

Let users add or delete rows only when the workflow needs it

If the table is acting like a lightweight spreadsheet, num_rows is the setting that matters most.

import pandas as pd
import streamlit as st
 
df = pd.DataFrame(
    [
        {"name": "Alice", "quota": 10},
        {"name": "Ben", "quota": 12},
    ]
)
 
edited_df = st.data_editor(
    df,
    num_rows="dynamic",
    width="stretch",
)

Use this carefully. Dynamic rows are useful for planning sheets and admin tools, but they can also make validation and downstream logic more complex.

Large tables: what actually helps

Large tables are where good intentions often turn into poor app experience.

The most practical fixes are usually:

  • show only the columns users need first
  • filter before rendering
  • cap height instead of dumping a giant scrolling page
  • cache data loading with st.cache_data
  • use selection and drill-down views instead of trying to show everything at once

This example keeps the table focused by slicing the data before display.

import pandas as pd
import streamlit as st
 
df = pd.read_csv("large_dataset.csv")
 
page_size = 100
page = st.number_input("Page", min_value=1, value=1)
start = (page - 1) * page_size
end = start + page_size
 
st.dataframe(df.iloc[start:end], height=400, width="stretch")

For user experience, this is often better than trying to prove the app can render everything at once.

st.dataframe vs st.table vs st.data_editor

NeedBetter choice
Interactive read-only tablest.dataframe
Static small tablest.table
Editable gridst.data_editor

This comparison is more useful than memorizing every argument. Start with the interaction model you need.

A more exploratory alternative: PyGWalker

st.dataframe is one way to render a dataframe inside Streamlit.

If you want a more exploratory interface, PyGWalker is another useful path. Instead of only showing the dataframe as a grid, PyGWalker can render it as a richer interactive analysis surface inside Streamlit.

That is useful when the reader wants more than inspection and light editing.

  • pygwalker.table(...) gives you a richer table view with column profiling in the header
  • pygwalker.walk(...) gives you a drag-and-drop, Tableau-like exploration interface for the dataframe

If your app is moving from "show the table" to "let the user explore the table visually," this is often the more natural upgrade path.

See:

Common mistakes

1. Using st.data_editor when nobody should edit anything

If the table is read-only, st.dataframe is usually clearer and simpler.

2. Fighting table formatting with CSS

Most formatting problems are better solved with column_config, hide_index, column_order, and sensible widths.

3. Showing too much data at once

Even if the app can render a huge table, that does not mean it is the best interface.

4. Mixing incompatible types in one editable column

st.data_editor can become hard or impossible to edit when a single column mixes types.

5. Forgetting that editing changes the returned data

st.data_editor returns the edited result. If you ignore that return value, the UI may look editable while your app logic still uses stale data.

Troubleshooting

Why is my table not full width?

Use width="stretch". The old use_container_width=True pattern is deprecated.

Why won't one of my st.data_editor columns edit correctly?

Check for mixed types, unsupported types, or a disabled configuration that is locking the column.

Can I style everything with pandas Styler?

No. Streamlit supports useful styling, but not every advanced Styler feature maps cleanly to the interactive table.

How do I let users pick rows from a dataframe?

Use on_select with a selection mode such as "single-row" or "multi-row" on st.dataframe.

Should I use a dataframe as both the full database and the full UI?

Usually no. It is often better to show a focused slice for browsing and keep broader logic in cached loaders, filters, or backend queries.

Related Guides

Frequently Asked Questions

What does st.dataframe do in simple terms?

It displays tabular data as an interactive table inside a Streamlit app.

What is the difference between st.dataframe and st.data_editor?

st.dataframe is mainly for viewing and selecting data. st.data_editor is for editing data and returning the edited result.

How do I make a Streamlit dataframe full width?

Use width="stretch" on st.dataframe or st.data_editor.

How do I format Streamlit dataframe columns?

Use column_config for labels, formats, widths, visibility, and editing rules.

Can users select rows in a Streamlit dataframe?

Yes. Enable selection with on_select and choose a selection_mode such as single-row or multi-row.