Skip to content

โ†”๏ธย ย Resizable Columns

Submitted by streamlit-extras

Summary

Drag-to-resize columns, a drop-in replacement for st.columns with interactive dividers.

Functions

resizable_columns

Display resizable columns that users can drag to adjust widths.

Works like st.columns but with draggable dividers between columns. Column widths are persisted across reruns.

Parameters:

Name Type Description Default
num_columns int | Sequence[int | float]

Number of columns (int) or list of initial width ratios (e.g. [1, 2, 1]).

2
min_width int

Minimum column width in pixels. Prevents collapsing columns to zero.

50
border bool

If True, show a border around each column.

False
key str | None

Unique key for this widget instance.

None

Returns:

Type Description
list[DeltaGenerator]

A list of column containers (DeltaGenerator) to place content in.

Example:

```python
cols = resizable_columns(3)
cols[0].write("Left")
cols[1].write("Center")
cols[2].write("Right")
```

Raises:

Type Description
StreamlitAPIException

If less than 1 column is specified.

Source code in src/streamlit_extras/resizable_columns/__init__.py
@extra
def resizable_columns(
    num_columns: int | Sequence[int | float] = 2,
    *,
    min_width: int = 50,
    border: bool = False,
    key: str | None = None,
) -> list[DeltaGenerator]:
    """Display resizable columns that users can drag to adjust widths.

    Works like st.columns but with draggable dividers between columns. Column widths
    are persisted across reruns.

    Args:
        num_columns: Number of columns (int) or list of initial width ratios (e.g. [1, 2, 1]).
        min_width: Minimum column width in pixels. Prevents collapsing columns to zero.
        border: If True, show a border around each column.
        key: Unique key for this widget instance.

    Returns:
        A list of column containers (DeltaGenerator) to place content in.

    Example:

        ```python
        cols = resizable_columns(3)
        cols[0].write("Left")
        cols[1].write("Center")
        cols[2].write("Right")
        ```

    Raises:
        StreamlitAPIException: If less than 1 column is specified.
    """
    if isinstance(num_columns, int):
        n = num_columns
        initial_widths = [1.0 / n] * n
    else:
        ratios = list(num_columns)
        n = len(ratios)
        total = sum(ratios)
        initial_widths = [r / total for r in ratios]

    if n < 1:
        raise st.errors.StreamlitAPIException("Must have at least 1 column.")

    effective_key = key or "resizable_columns_default"
    component_state = st.session_state.get(effective_key, {})
    stored_widths = component_state.get("widths")

    widths = [float(w) for w in stored_widths] if stored_widths and len(stored_widths) == n else initial_widths

    component = _get_component()
    component(
        key=effective_key,
        data={
            "num_columns": n,
            "widths": widths,
            "min_width": min_width,
        },
        default={"widths": initial_widths},
        on_widths_change=_on_widths_change,
        height=1,
    )

    result_state = st.session_state.get(effective_key, {})
    final_widths = result_state.get("widths", widths)
    col_spec = [max(w, 0.01) for w in final_widths] if final_widths and len(final_widths) == n else widths

    return list(st.columns(col_spec, border=border))

Import:

from streamlit_extras.resizable_columns import resizable_columns # (1)!
  1. You should add this to the top of your .py file ๐Ÿ› 

Examples

example_with_border

def example_with_border() -> None:
    """Resizable columns with borders."""
    st.write("### Bordered resizable columns")
    cols = resizable_columns(3, border=True, key="border_demo")
    with cols[0]:
        st.metric("Revenue", "$12.4k", "+8%")
    with cols[1]:
        st.metric("Users", "1,024", "+12%")
    with cols[2]:
        st.metric("Uptime", "99.9%", "+0.1%")

example_basic

def example_basic() -> None:
    """Basic resizable columns."""
    st.write("### Drag the handles between columns to resize")
    cols = resizable_columns(3, key="basic_demo")
    with cols[0]:
        st.header("Left")
        st.write("This is the left column. Drag the divider to resize.")
    with cols[1]:
        st.header("Center")
        st.write("This is the center column.")
    with cols[2]:
        st.header("Right")
        st.write("This is the right column.")

example_with_ratios

def example_with_ratios() -> None:
    """Columns with initial width ratios."""
    st.write("### Custom initial ratios [1, 3, 1]")
    cols = resizable_columns([1, 3, 1], key="ratio_demo")
    with cols[0]:
        st.metric("Sidebar", "42")
    with cols[1]:
        st.line_chart({"data": [1, 5, 2, 6, 2, 1]})
    with cols[2]:
        st.metric("Info", "99%")

example_two_panel

def example_two_panel() -> None:
    """Two-panel resizable layout."""
    st.write("### Two-panel layout")
    left, right = resizable_columns([1, 2], key="two_panel_demo")
    with left:
        option = st.radio("Select view", ["Chart", "Table", "Summary"])
        st.write(f"Selected: {option}")
    with right:
        st.write("Main content area")
        st.bar_chart({"values": [3, 6, 2, 8, 4]})