Issue with Modifying Text using st.text_input and st.button

I’m encountering an issue with Streamlit where I’m trying to allow the user to modify text using st.text_input and then display the modified text when a button is clicked. However, the modified text is not persisting as expected in the session state.

Here’s a simplified version of the code:

import streamlit as st

# Initialize session state
if 'text' not in st.session_state:
    st.session_state.text = "original"

if st.button("show"):
    # Allow the user to modify the text
    st.session_state.text = st.text_input("Edit Text", value=st.session_state.text)

# Display the modified text
st.markdown(st.session_state.text)

if st.button("show again"):
    # Display the modified text
    st.markdown(st.session_state.text)

Despite using st.text_input to modify the text, the “show again” button still displays the original text, not the modified one. I’ve tried using st.text_area as well, but the issue persists.

Can anyone help me understand why the modified text is not persisting in the session state as expected? Any guidance or suggestions would be greatly appreciated!

hi @Raphael_Fabre

The issue you are encountering is related to the way Streamlit handles the session state and the rendering of elements. When you use st.text_input or similar widgets inside a button callback, the changes made by the user are not immediately reflected in the session state. This is because Streamlit does not re-run the entire script in response to a button click.

To address this, you can use the text_input widget directly to capture the user input, and then update the session state with that input.

Here’s an updated version of your code that should work as expected:

import streamlit as st

# Initialize session state
if 'text' not in st.session_state:
    st.session_state.text = "original"

# Allow the user to modify the text
modified_text = st.text_input("Edit Text", value=st.session_state.text)

# Update session state when the user modifies the text
if modified_text != st.session_state.text:
    st.session_state.text = modified_text

# Display the modified text
st.markdown(st.session_state.text)

if st.button("show again"):
    # Display the modified text
    st.markdown(st.session_state.text)

Note:
Streamlit’s session state handling is based on variable equality, so it’s essential to check for changes explicitly if you want to update the session state based on user input.

I’m facing something similar to this issue, actually pretty sure it’s the same problem but with different direction with components and state handling.

I have a st.input cached into a variable and then a st.button to send the cached variable to a firebase firestore connection. The code is something like:

def save_info(info):
  firebase_save_info(info)

myvar = st.input("My label", "Placeholder")
st.button("Send", on_click=save_info, args=[myvar])

The real problem is:
when I change the value in the input field and click directly in the button, it’ll send an empty string. If I do click somewhere else before the button, streamlit will run the value change in input and then it’ll send the correct info in the button args.

Guess what the user will do instead of clickg somewhere… Does anyone know any workaround for that?

I can’t reproduce the issue with your code.

I was trying to set up my example and I was afraid that I was saying something wrong just in my mind. So I re-run the tests and actually, my example would work pretyy fine, but my real code is a little bit messy.

The real code is somehitng like this:

import streamlit as st

def save_info(info):
  print(info)

columns = st.columns(2)

for i in range(len(columns)):
    with columns[i]:
        myvar = st.text_input("My label", placeholder="Placeholder", key="another-%d"%i)
        st.button("Send", on_click=save_info, args=[myvar], key="something-%d"%i)

myvar

and the problem is inside this range loop I guess.

I saw another answer in another thread saying that we can’t ‘force’ the value in any input widget do update itself. So, the question stays the same, how to handle the text_input update in a range loop?

This might sound funny but I found the solution using the key argument from text_input. I’m learning Streamlit now and the docs sounds a little confusing. I’m gonna share my current test to set everythign working.

columns = st.columns(2)

for i in range(len(columns)):
    with columns[i]:
        info = st.text_input("My label",
            placeholder= "Placeholder",
            key= "another%d"%i)

st.session_state["another0"]

Thank you for your answers and directions in the other thread. I recommend anyone having this trouble to see the linked thread above and watch the video about streamlit states.

I have encountered a very similar problem as reported by @Raphael_Fabre. @Hiro’s code works on my machine, but my code does not, even when modified explicitly according to Hiro’s protocol. Even when I check for changes between text_input input and output explicitly, there are no differences. It appears that text_input is not outputting updated values. Does this mean that this function has been corrupted somehow?

I share my code below. It is for a list editor. It is based on @Subrat_Mohapatra’s data editor example. I had decided that data_editor could not provide the functionality that I was looking for and that I should write my own list editor widget. I figured that would be easy-peasy. Little did I suspect!

I have not added the move up and move down functionality yet. That should be no problem. But when I try to add or edit a record I am unable to get updated input. It is the darnedest thing. What could be simpler than line data input widget. I have used them many times before with no problem. I have no idea what’s going on!

# started from https://discuss.streamlit.io/t/how-to-add-delete-a-new-row-in-data-editor-in-streamlit/70608
import streamlit as st
import pandas as pd
import sys
import os
from datetime import datetime as dt

def findSel(zItem, zList):
    for i in range(0,len(zList)):
        if zItem==zList[i]:
            return(i)
    return(None)
    
if 'pIndex' not in st.session_state:
    st.session_state.pIndex=0
    st.session_state.add=False
    st.session_state.i=0

# Create a variable to hold the dataframe. Initialize it with the given list.
if 'zList' not in st.session_state:
    list0 = [{'name': 'Love', 'year':1991},
        {'name': 'Smells Like Teen Spirit', 'year': 1991},
        {'name': 'Lithium', 'year': 1992},
        {'name': 'All Apologies', 'year': 1993},
        {'name': 'Stay Away', 'year': 1993}]
    st.session_state.df = pd.DataFrame(list0)
    st.session_state.zList=list(st.session_state.df['name'])

st.header('List Editor')

with st.container(border=True):
    
    col1, col2 = st.columns([2, 1], gap='medium')
    
    with col1:
        # added num_rows='dynamic'
        st.session_state.resp = st.radio(label="---", options=st.session_state.zList, index=st.session_state.pIndex, label_visibility='collapsed')
        st.session_state.i+=1
        print("now, i, resp", dt.now(), st.session_state.i, st.session_state.resp)
        st.session_state.lenList=len(st.session_state.zList)
        if st.session_state.resp:
            st.session_state.pIndex=findSel(st.session_state.resp,st.session_state.zList)
            print("resp, pIndex", st.session_state.resp, st.session_state.pIndex)
            st.session_state.add=False

    with col2:
        Edit = st.button("Edit", use_container_width=True)
        st.session_state.Add = st.button("Add", use_container_width=True)
        Delete = st.button("Delete", use_container_width=True)
        MoveUp = st.button("Move Up", use_container_width=True)
        MoveDown = st.button("Move Down", use_container_width=True)
        save = st.button('Save to data_temp.csv')

    #if st.session_state.Add or st.session_state.add:
    if True:
        st.session_state.add=True
        cz1, cz2 = st.columns(2)
        with cz1:
            if st.button("Insert Above", use_container_width=True):
                newItem=st.text_input("Enter list value", "____", key=101)
                if newItem:
                    print("newItem above", newItem)
                    st.session_state.zsList=st.session_state.zList.insert(st.session_state.pIndex, newItem)
                    print("zList", st.session_state.zList)
                    #st.session_state.add=False
        with cz2:
            if st.button("Insert Below", use_container_width=True):
                st.session_state.newItem=st.text_input("Enter list value", "____", key = 102)
                if st.session_state.newItem:                    
                    print("newItem", st.session_state.newItem)
                    st.session_state.zsList=st.session_state.zList.insert(st.session_state.pIndex+1, st.session_state.newItem)
                    print("zList below", st.session_state.zList)
                    #st.session_state.add=False

if Edit:
    if st.session_state.pIndex:
        st.session_state.targ=st.session_state.zList[st.session_state.pIndex]
        changedItem = st.text_input("Enter list value", st.session_state.zList[st.session_state.pIndex],key='EDIT')
        if changedItem != st.session_state.targ:
            print("changedItem", changedItem)
            st.session_state.zList[st.session_state.pIndex]=changedItem
            print("zList", st.session_state.zList)

if Delete:
    del st.session_state.zList[st.session_state.pIndex]
    if st.session_state.pIndex==st.session_state.lenList-1:
        st.session_state.pIndex-=1
        st.session_state.lenList-=1
    st.rerun()

if save:
    with open('listTest.csv', 'wb') as myfile:
        import csv
        wr = csv.writer(myfile, quoting=csv.QUOTE_ALL)
        wr.writerow(st.session_state.zList)

if 'df' not in st.session_state:
    st.session_state.zList = list(pd.read_csv('listTest.csv'))