How to display st.chat_input() widget in different locations other than the bottom of the page?

I am trying to build a pdf chatbot. I want the st.chat_input() to be in the middle of the top section above the file upload button. But whatever method I tried to keep the st.chat_input() widget, only shows at the bottom. What should I do like CSS styling or other methods so that I can get this widget displayed at the position I want?

This is the sample for reference. Thanks.

import streamlit as st

st.title("Chat with the documents ๐Ÿ’ฌ ๐Ÿ“š")

input = st.chat_input("What would you like to know about this document?")

# File uploader with clear instructions
uploaded_file = st.file_uploader("Upload PDF โฌ†๏ธ", type="pdf")

css = '''
    <style>
    [data-testid='stFileUploader'] {
        display: flex;
        align-items: center;
    }
    [data-testid='stFileUploader'] section {
        padding: 0;
    }
    [data-testid='stFileUploader'] section > input + div {
        display: none;
    }
    [data-testid='stFileUploader'] section + div {
        margin-left: 1cm; /* Adjust spacing for browse button */
        margin-right: auto; /* Push uploaded file name to the right */
    }
    </style>
    '''


st.markdown(css, unsafe_allow_html=True)

if uploaded_file is not None:
    # Access the file content (as bytes)
    file_bytes = uploaded_file.read()
else:
    st.write("No file uploaded yet.")

1 Like

Hi @rama

Thereโ€™s a code snippet from @blackary that shows how to do this using the streamlit-extras component:

Hope this helps!

2 Likes

Hello @dataprofessor
Thanks a lot for the reply. I tried using the code you provided. But still, the chat input is at the bottom, though the upload button is coming below the chat_input box.

import streamlit as st
from streamlit_extras.stylable_container import stylable_container

st.title("Chat with the documents ๐Ÿ’ฌ ๐Ÿ“š")

if "messages" not in st.session_state:
    st.session_state["messages"] = []


def generate_response(prompt):
    return f"This is a response to: {prompt}", 0, 0, 0


for message in st.session_state["messages"][1:]:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

if prompt := st.chat_input("Type your message..."):
    st.session_state["messages"].append({"role": "user", "content": prompt})
    st.chat_message("user").write(prompt)

    with st.spinner("Thinking..."):
        (
            full_response,
            total_tokens,
            prompt_tokens,
            completion_tokens,
        ) = generate_response(prompt)

    st.chat_message("assistant").write(full_response)
    st.session_state["messages"].append({"role": "assistant", "content": full_response})

with stylable_container(
    key="bottom_content",
    css_styles="""
        {
            position: fixed;
            bottom: 10px;
        }
        """,
):
    uploaded_file = st.file_uploader("Upload PDF โฌ†๏ธ", type="pdf")

css = '''
<style>
[data-testid='stFileUploader'] {
    display: flex;
    align-items: center;
}
[data-testid='stFileUploader'] section {
    padding: 0;
}
[data-testid='stFileUploader'] section > input + div {
    display: none;
}
[data-testid='stFileUploader'] section + div {
    margin-left: 1cm; /* Adjust spacing for browse button */
    margin-right: auto; /* Push uploaded file name to the right */
}
</style>
'''

st.markdown(css, unsafe_allow_html=True)

st.markdown(
    """
    <style>
        .stChatFloatingInputContainer {
            bottom: 20px;
            background-color: rgba(0, 0, 0, 0)
        }
    </style>
    """,
    unsafe_allow_html=True,
)


This is how the UI is looking for the above code. There is still a lot of white space between the title and the input widget. I want the chat input widget just below the title. I also observed that the cross :heavy_multiplication_x: icon is not clickable. Kindly excuse me if I make any mistakes in the code and correct me where the issue is. Thank you.

1 Like

Hi!
I have a similar question:
This is how my screen looks on iPhone:


shat_input field hiding behind the Safary toolbar.
I wanted to lift it up a bit, tried the approach with stylable_container, but it doesnโ€™t work for me.
The stylable_container appears just beneath the chat_input.
So, how I can lift the chat_input by 30 pixels?

P.S.
After the first message, the chat_input takes the correct position.
But it isnโ€™t very clear to the user how to startโ€ฆ

1 Like

Exactky same issue with my users too in various browsers/OS.
When u check at localhost, this issue is not present. So u wonโ€™t notice such usability issue unless u deploy it. The gap between top contents and input field extends which is pushing down the chat_input field behind the streamlit logo and browser bar.

Some users even through thr red streanlit logo as the submit button which is causing more UX issue.

Since problem is similar, so Im not adding my screenshots now , but still if u need it to solve, I will be happy to share.

1 Like

It would be great if you could share the solution. Appreciate it!

1 Like

Hey! I have found a solution!
I looked at how is chat_imput implemented and found _bottom

from streamlit import _bottom

if user_question := st.chat_input("Enter a question here",):
                        handle_userinput(user_question)
_bottom.button('tap here')

and you will see the button under the chat_input
*All widgets are available, not only button

4 Likes

good idea, thanks

Well, thank you, that is excellent workaround, but the _bottom dg is a protected member, so you are risking that it may be removed or behaviour changed in the future. But for now this is the best solution of the problem and I am going to use it for the file uploader in my AI app at http://sigmaai.zapto.org โ€“ thanks again!