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?