Have a text_area automatically scroll to the bottom

I am running Streamlit 1.44.1 locally on Python 3.11.11. I am using the technique described here for streaming output to a text area, and it works as shown in that post.

My problem is that I am trying to use this technique to stream a kind of log output, so as the text area is updated I want the viewport to follow the latest updates to the text at the bottom.

Instead, what currently happens is that the viewport always resets to the very top of the text on every update, so following the text as it streams in at the bottom is impossible:

stream

Here’s the test code, copied from the original post I linked to above and modified to make the text area a bit shorter.

Test code
import streamlit as st
import time

# Define the text to simulate typing
_LONG_TEXT = """Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you
"""

# Define a generator function to yield text incrementally
def stream_text():
    current_text = ""
    for char in _LONG_TEXT:
        current_text += char
        yield current_text
        time.sleep(0.01)  # Adjust the speed of typing

# Define the function to update the text area
def generate_description():
    text_generator = stream_text()
    text_area = st.empty()
    for text in text_generator:
        text_area.text_area('Description', value=text, height=80)
        time.sleep(0.01)  # Adjust the speed of typing

# Ensure the session state has a key for description
if 'description' not in st.session_state:
    st.session_state.description = ''

# Display the text area with the current session state value
text_area_placeholder = st.empty()

# Button to start the typing simulation
if st.button('Generate Description'):
    generate_description()

Is there a way to get the viewport to “follow” the text as it streams in at the bottom?

OK, I figured out a solution via chat_message, which appears to get special treatment by Streamlit in that updates to a container containing chat messages don’t cause the container to be re-scrolled back to the top.

stream-chat

This works for me since the output I want to stream does happen to come from an assistant, so I don’t mind the bot icon that gets prepended to each line. But this is not a fully general solution that will work in other use cases.

I also had to add some custom CSS to reduce the excessive padding put around each chat message. But overall this works well for my use case.

Source Code
import streamlit as st
import time

_LONG_TEXT = """Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you
"""

# Dunno if there is a better way to do this, but the default display of chat
# messages adds a lot of superfluous padding.
st.html("""
<style>
.stChatMessage {
    padding-top: 0;
    padding-bottom: 0;
}
</style>
""")

def generate_description():
    with st.container(height=100):
        for line in _LONG_TEXT.splitlines():
            chat_line = ""
            with st.chat_message("assistant"):
                chat_message = st.empty()
                for char in line:
                    chat_line += char
                    chat_message.markdown(chat_line)
                    time.sleep(0.01)

if st.button('Stream Description'):
    generate_description()

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.