Today, I upgraded Streamlit from version 1.40.2 to 1.41.0. While running my code, I encountered an unexpected behavior:
I created a st.tabs
object with three tabs, each containing several interactive components (st.checkbox
, st.toggle
, etc.). After starting the application, the page correctly displayed the first tab. However, the first time I interacted with a component in the second or third tab, the application unexpectedly redirected to the first tab. Subsequent interactions within the second or third tabs did not cause this redirection.
I initially suspected interference from other components or code. Even after simplifying the second and third tabs to contain only a single interactive component each, the first interaction with a component in those tabs still caused a redirection to the first tab. Finally, rolling back Streamlit to version 1.40.2 resolved the issue.
I cannot reproduce this. Works as expected for me.
You can use st.pills instead of st.tab. it help a lot as st.tab is just a design trick. With st.pills. you will be able to control more accurately what is display.
Thank you very much for your suggestion! I also sincerely apologize for not expressing my gratitude for your reply in a timely manner! I tried using st.pills
and st.tabs
, but I noticed that if I perform some widget operations in one option of st.pills
(e.g., Option A), and then switch to another option and return to Option A, the widgets in Option A are reinitialized and do not retain their previous state. This issue does not occur with st.tabs
.
Hello
you will need to save the state of the widgets of option A and option B in the session state in order to retain their state for each rereun :
Let me help you create a solution for replacing Streamlit tabs with pills while maintaining widget state.
import streamlit as st
from typing import List, Optional, Callable
import functools
class PillState:
“”“Manages state for pills to replicate tab behavior”“”
def init(self, key: str):
self.key = key
if key not in st.session_state:
st.session_state[key] = 0
def get_active_pill(self) -> int:
return st.session_state[self.key]
def set_active_pill(self, index: int):
st.session_state[self.key] = index
def pills(tabs: List[str], key: Optional[str] = None) → int:
“”"
Create pills that behave like tabs, maintaining widget state
Args:
tabs: List of tab/pill labels
key: Optional unique key for the pills group
Returns:
Index of currently selected pill
"""
if key is None:
key = f"pills_{id(tabs)}"
state = PillState(key)
# Create horizontal container for pills
cols = st.columns(len(tabs))
for i, (tab, col) in enumerate(zip(tabs, cols)):
# Custom styling for active/inactive states
is_active = state.get_active_pill() == i
bg_color = "primary" if is_active else "secondary"
# Create clickable pill button
if col.button(
tab,
key=f"{key}_{i}",
type=bg_color,
use_container_width=True
):
state.set_active_pill(i)
return state.get_active_pill()
Example usage with widget state persistence
def main():
st.title(“Pills Demo with State Management”)
# Create pills
tabs = ["Tab 1", "Tab 2", "Tab 3"]
active_tab = pills(tabs, key="main_pills")
# Content for each tab with persistent widgets
if active_tab == 0:
st.write("### Tab 1 Content")
st.number_input("Enter a number", key="num_input_1")
st.text_input("Enter text", key="text_input_1")
elif active_tab == 1:
st.write("### Tab 2 Content")
st.slider("Select value", 0, 100, key="slider_2")
st.checkbox("Check me", key="checkbox_2")
else:
st.write("### Tab 3 Content")
st.radio("Choose option", ["A", "B", "C"], key="radio_3")
st.selectbox("Select item", ["Item 1", "Item 2"], key="select_3")
if name == “main”:
main()
This solution provides a seamless way to replace Streamlit’s st.tabs
with pills while maintaining widget state. Here’s how it works:
- The
PillState
class manages the active pill index in Streamlit’s session state - The
pills
function creates horizontally arranged buttons styled as pills - Each pill uses Streamlit’s button component with conditional styling based on active state
- Widget state is preserved by using consistent keys for all components
Interacting with input elements outside of form context causes the whole application to re-load from top to bottom. This is an expected behaviour from Streamlit’s core framework.
A quick solution here is to add these elements inside a form. st.form - Streamlit Docs
This will prevent the whole application from re-loading, which is the cause of your issue - navigating to first tab.