Bug with st.tabs(), glitches for 1 frame while rendering

On the first rendering of items in tabs that are long, they glitch out for a brief moment. This is what they look like in that moment:

Video demonstration:

As you can see, this only happens the first time they are rendered, they’re fine after that.

Example code:

import streamlit as st
st.write("##  Example site")

tab1, tab2, tab3, tab4, tab5 = st.tabs(
    [
        "Tab 1",
        "Tab 2",
        "Tab 3",
        "Tab 4",
        "Tab 5",

    ]
)

with tab1:
    form = st.form("tab 1 form", clear_on_submit=True)
    uploaded_files = form.file_uploader("Upload files")
    form.form_submit_button("submit")

with tab2:
    form = st.form("tab 2 form", clear_on_submit=True)
    uploaded_files2 = form.file_uploader("Upload files")
    form.form_submit_button("submit")

with tab3:
    st.write("# Hi")
    st.write("## make this longer")

with tab4:
    st.write("# fine")

with tab5:
    st.write("# This is not fine because it is long")
    
2 Likes

Any ideas? Could someone point me to this part of the source code maybe?

This even happens on the st.tabs documentation page (st.tabs - Streamlit Docs):


I think this might be what you’re looking for.

Thanks, did some traversal and I think it’s this autosizer that is causing this problem:

I think it is just taking too long to calculate the proper width.

Hi both @kevinlinxc , @willhuang ,

FYI, I opened an issue for this on Github:

Did you by any chance find a workaround or solution? Thanks in advance.

5 Likes

@kevinlinxc @marduk

Having the same problem right now, I am happy to hear that it’s a width problem and not a “data not initializing problem”.

With my case the first tab shows all elements correctly, but additional tabs only show the text. (Note the tabs are inside of an expander)

I have noticed that if I create a function to display a tabs elements it works fine — but it’s not sufficient in my case.

I’ll be looking into this a good amount tomorrow and will update you guys if I get anything working.

1 Like

I never found anything, sorry. My JS knowledge is pretty limited and it seems like this a pretty niche issue even if I knew more about React

2 Likes

Bumping this in case someone found a temporary workaround.

And for those interested, please remember to like the GitHub issue to signal interest :slight_smile:

2 Likes

Here is a possible solution:

import streamlit as st
from streamlit.components import v1

st.title('Content inside tabs jitters on first load')
t1, t2, t3 = st.tabs(['First tab', 'Second tab', 'Third tab'])

t1.header('This content is inside the first tab')
t2.header('This content is inside the second tab')
t3.header('This content is inside the third tab')

v1.html("""
<script>
const tabs = window.parent.document.querySelectorAll('button[data-baseweb="tab"] p');
const tab_panels = window.parent.document.querySelectorAll('div[data-baseweb="tab-panel"]');

tabs.forEach(function (tab, index) {
    const tab_panel_child = tab_panels[index].querySelectorAll("*");

    function set_visibility(state) {
        tab_panels[index].style.visibility = state;
        tab_panel_child.forEach(function (child) {
            child.style.visibility = state;
        });
    }

    tab.addEventListener("click", function (event) {
        set_visibility('hidden')

        let element = tab_panels[index].querySelector('div[data-testid="stVerticalBlock"]');
        let main_block = window.parent.document.querySelector('section.main div[data-testid="stVerticalBlock"]');
        const waitMs = 1;

        function waitForLayout() {
            if (element.offsetWidth === main_block.offsetWidth) {
                set_visibility("visible");
            } else {
                setTimeout(waitForLayout, waitMs);
            }
        }
        waitForLayout();
    });
});
</script>
""", height=0)
import streamlit as st
from streamlit.components import v1

st.title('Content inside tabs jitters on first load')
t1, t2, t3 = st.tabs(['First tab', 'Second tab', 'Third tab'])

t1.header('This content is inside the first tab')
t2.header('This content is inside the second tab')
t3.header('This content is inside the third tab')

v1.html("""
<script>
function checkElements() {
    const tabs = window.parent.document.querySelectorAll('button[data-baseweb="tab"] p');
    const tab_panels = window.parent.document.querySelectorAll('div[data-baseweb="tab-panel"]');

    if (tabs && tab_panels) {

        tabs.forEach(function (tab, index) {
            const tab_panel_child = tab_panels[index].querySelectorAll("*");

            function set_visibility(state) {
                tab_panels[index].style.visibility = state;
                tab_panel_child.forEach(function (child) {
                    child.style.visibility = state;
                });
            }

            tab.addEventListener("click", function (event) {
                set_visibility('hidden')

                let element = tab_panels[index].querySelector('div[data-testid="stVerticalBlock"]');
                let main_block = window.parent.document.querySelector('section.main div[data-testid="stVerticalBlock"]');
                const waitMs = 1;

                function waitForLayout() {
                    if (element.offsetWidth === main_block.offsetWidth) {
                        set_visibility("visible");
                    } else {
                        setTimeout(waitForLayout, waitMs);
                    }
                }

                waitForLayout();
            });
        });
    } else {
        setTimeout(checkElements, 100);
    }
}

checkElements()
</script>
""", height=0)

Would Streamlit consider just including your function in their source code, or would it be considered too hacky?

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