Fix st.form to the bottom of the st.container

Hi all! As the title says, I have a st.form that includes a st.text_input and a st.form_submit_button. The form is embedded as part of the st.container, and I want to keep it at the bottom of the page as the input field for a GPT model (similar to that of ChatGPT).

Here鈥檚 a simplified example of my current code:

with st.container():
    with st.form("input_form"):
        col1, col2 = st.columns([9, 1])

        with col1:
            st.text_input("Please enter what you want to tell me here: ")

        with col2:
            st.form_submit_button()

I鈥檓 thinking about adding CSS styles to these elements, but don鈥檛 know exactly how. Any help would be much appreciated!

Anyone?

Still no luck

Another day of no luck :frowning:

Is there a particular reason to use a st.form instead of a st.chat_input (st.chat_input - Streamlit Docs)?


For a form. you could set the position of the div[data-testid="stForm"] absolute, however, its interaction with other elements in the page will get tricky. Here an example:

Code:
import streamlit as st
from io import StringIO
from datetime import datetime

st.markdown(
    """
    <style>
    div[data-testid="stForm"]{
        position:fixed;
        right: 10%;
        left: 10%;
        bottom: 8%;
        border: 2px solid green;
        background-color: #EEEEEE;
        padding: 10px;
        z-index: 10;
    }
    </style>
    """, unsafe_allow_html=True
)

if "chat_history" not in st.session_state:
    st.session_state.chat_history = StringIO()

with st.form("input_form"):
    "*Enter messages here*"

    col1, col2 = st.columns([6, 1])

    with col1:
        message = st.text_input("message", label_visibility="collapsed")
        
        if message:
            st.session_state.chat_history.write(datetime.now().strftime(r"%H:%M:%S"))    
            st.session_state.chat_history.write(f":  {message}\n")

    with col2:
        submitted = st.form_submit_button(use_container_width=True)

if submitted:
    st.text_area("Chat history", st.session_state.chat_history.getvalue(), height=200)
2 Likes

Hi, thank you very much for your reply! The reason I didn鈥檛 choose st.chat_input is that it currently doesn鈥檛 support custom starter prompt (at least based on the answers I receive on my previous post), but st.text_input does support it. I tried your code, while it did fix the input box to the bottom of the page, the chat history seemed to ignore the input box and cut through it as if it didn鈥檛 exist:

Here鈥檚 the relevant code piece:

with open('style.css') as f:  # This is where I put your CSS code
    st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)


init_prompt = st.selectbox(
    'You might want to try these prompts...',
    ['How to socialize?',
     'How to focus on tasks?',
     'How to find peace in daily work?']
)
chat_room = st.container()

if 'history' not in st.session_state:
    st.session_state['history'] = []

for msg in st.session_state['history']:
    chat_room.chat_message(msg['role']).write(msg['content'])


def add_to_history(content: str, role: str) -> None:
    st.session_state['history'].append(
        {'role': role, 'content': content}
    )


def get_gpt_response(prompt: str) -> Iterator[dict]:
    return openai.ChatCompletion.create(
        model='gpt-3.5-turbo',
        messages=[{'role': 'user', 'content': prompt}],
        temperature=0.5,
        stream=True
    )


instr = 'Hi there! Enter what you want to let me know here.'
with st.form('chat_input_form'):
    input_col, btn_col = st.columns([8.5, 1])

    with input_col:
        prompt = st.text_input(
            instr,
            value=init_prompt,
            placeholder=instr,
            label_visibility='collapsed'  # Hide the label
        )

    with btn_col:
        submitted = st.form_submit_button('Chat')

    if prompt and submitted:
        chat_room.chat_message('user').markdown(prompt)
        add_to_history(prompt, 'user')

        with chat_room.chat_message('assistant'):
            msg = st.empty()
            full_res = ''
            gpt_res = get_gpt_response(ND_PROMPT + prompt)
            next(gpt_res)  # Remove the role entry
            for chunk in gpt_res:
                if new_text := chunk['choices'][0]['delta']:
                    full_res += new_text['content']
                    msg.markdown(full_res + ':rainbow:')
                else:
                    # We hit the end of the GPT response
                    msg.markdown(full_res)

            add_to_history(full_res, 'assistant')

I want the chat history to respect the input box and always stay on top of it. Alternatively, putting the custom starter prompt inside st.chat_input also works. Do you mind taking a look at this and give me some hints to achieve either one of these options?

Could split the document such that the main app occupies only the top and keep the st.form fixed to the bottom. But that is probably not the best solution, I鈥檇 expect it to break on mobile or any small window, or if a sidebar is added鈥

brokenLayout

Code:
import streamlit as st
from datetime import datetime

st.markdown(
    """
    <style>
    div[data-testid="stAppViewContainer"]{
        position:fixed;
        bottom:18%;
        padding: 10px;
    }
    div[data-testid="stForm"]{
        position:fixed;
        right: 10%;
        left: 10%;
        bottom: 2%;
        border: 2px solid green;
        padding: 10px;
        z-index: 10;
    }
    </style>
    """, unsafe_allow_html=True
)

if "chat_history" not in st.session_state:
    st.session_state.chat_history = list()

with st.form("input_form"):
    "*Enter messages here*"

    col1, col2 = st.columns([6, 1])

    with col1:
        message = st.text_input("message", label_visibility="collapsed")
        
        if message:    
            st.session_state.chat_history.append(f"{datetime.now().strftime(r'%H:%M:%S')}:  {message}")

    with col2:
        submitted = st.form_submit_button(use_container_width=True)

if submitted:
    for msg in st.session_state.chat_history:
        with st.chat_message("user", avatar="馃お"):
            st.write(msg)

1 Like

Also, this sounds like a nice feature request you can make.

Thanks a lot! I鈥檒l try your code ASAP.

Oh and I just created a feature request here: Custom text value for st.chat_input 路 Issue #7166 路 streamlit/streamlit 路 GitHub. Feel free to give it a like :slight_smile:

1 Like