<img src="app/static/...> does not work

Hello, I am having a problem with using HTML in displaying an image that is saved in my GitHub repository g1_mdrrmo_wxdashboard. My Streamlit app is a multipage app, which I have already deployed on Community Cloud (link: https://g1-mdrrmo-wxdashboard.streamlit.app/). The requirements file is here: requirements.txt. The app’s homepage is “:house: Home.py” and the other pages located inside the “g1_mdrrmo_wxdashboard/pages” directory. I also have a “g1_mdrrmo_wxdashboard/.streamlit” folder, which contains “config.toml” which in turn contains:

[server]
enableStaticServing = true

I am using Streamlit version 1.49.1 and Python 3.11.5 for this project, especially when I was still developing this app on my computer.

I intended to add images files of warning icons in the “1_⚠️ Advisories & Warnings.py” page using HTML. And I need the warning icon to be centered inside its corresponding container, with a label below indicating the date and time of warning issuance. But since Streamlit apparently cannot directly set the horizontal alignment of text and images.

At first, I resorted to using HTML, which was easy when the files were inside my computer (I’ll just insert the absolute path). But knowing that I need to deploy this so that my colleagues can use the app, I need a way to cut the app’s dependency on files stored in my computer’s local storage. I first tried to upload the icons to Wikimedia Commons, but even though it worked okay (but putting the direct URL to the file), it puts a risk that some, if not all, of the files will be deleted, and not all files can be uploaded there.

Since Google Drive won’t work and I don’t have funds to use Amazon or Google Cloud Storage, I decided to store them to the “g1_mdrrmo_wxdashboard/static/” folder in my directory.

I tried revising the visuals for one of the image files to see if the suggested workarounds would work. I did this for the file “rain warning - 00 (tstm adv).png” (icon for Thunderstorm Advisory):

# Setting row 1
    wxrow1 = st.container(horizontal=True, horizontal_alignment='center')
    
    # Thunderstorm
    tstm = wxrow1.container(width=300)
    tstm.write('##### Thunderstorm')
    
    tstm_content = tstm.container(height=200, border=True)
    
    tstm_content.markdown(
        """
        <div style="display: flex; justify-content: center; align-items: center; height: 100%;">
            <img src="app/static/rain warning - 00 (tstm adv).png" width="145">
        </div>
        <div style="text-align: center; font-size: small;">
            as of 10:00 AM 31 August 2025
        </div>
        """,
        unsafe_allow_html=True
    )
    
    with tstm.expander('Details'):
        st.write(
            '''
            Moderate to heavy rainshowers with possible isolated intense downpours, accompanied with lightning and strong winds, are ongoing or likely to occur within 1-2 hours.
            '''
            )

But even though I already applied the format “”, and even though the file is present in the static folder, the file name and extension are correct, the file is not corrupted, and static file serving is enabled in .streamlit/config.toml, the image still does not appear, as shown in the following screenshot:

As shown in the screenshot, the Thunderstorm Advisory icon (“rain warning - 00 (tstm adv).png”) is not visible, but the other icons (which are dependent on the files I previously uploaded in Wikimedia Commons) are still visible.

I tried using the Streamlit docs assistant AI, but it explained that “the knowledge sources do not provide further troubleshooting steps for this specific scenario”.

I would greatly appreciate your help in this matter. Thank you very much in advance.

You appear to have app/static/static in the url, instead of just app/static – if you fix that, it should work fine

-            <img src="app/static/static/hvy rain outlook 2.png" width="145">
+            <img src="app/static/hvy rain outlook 2.png" width="145">

Hello, I already fixed the typo, and the problem still persists.

   # Setting row 1
    wxrow1 = st.container(horizontal=True, horizontal_alignment='center')

    # Thunderstorm
    tstm = wxrow1.container(width=300)
    tstm.write('##### Thunderstorm')

    tstm_content = tstm.container(height=200, border=True)

    tstm_content.markdown(
        """
        <div style="display: flex; justify-content: center; align-items: center; height: 100%;">
            <img src="app/static/rain warning - 00 (tstm adv).png" width="145">
        </div>
        <div style="text-align: center; font-size: small;">
            as of 10:00 AM 31 August 2025
        </div>
        """,
        unsafe_allow_html=True
    )

    with tstm.expander('Details'):
        st.write(
            '''
            Moderate to heavy rainshowers with possible isolated intense downpours, accompanied with lightning and strong winds, are ongoing or likely to occur within 1-2 hours.
            '''
            )

    # Rainfall
    rain = wxrow1.container(width=300)
    rain.write('##### Rainfall')
    rain_content = rain.container(height=200, border=True)

    rain_content.markdown(
        """
        <div style="display: flex; justify-content: center; align-items: center; height: 100%;">
            <img src="app/static/static/rain warning - 01 (rainfall advisory).png" width="145">
        </div>
        <div style="text-align: center; font-size: small;">
            as of 10:00 AM 31 August 2025
        </div>
        """,
        unsafe_allow_html=True
    )

    with rain.expander('Details'):
        st.write(
            '''
            Long-duration light to moderate rains with possible isolated heavy downpours are ongoing or likely to occur within 2-3 hours.
            '''
            )
.
.
.
.
    # Setting row 2
    wxrow2 = st.container(horizontal=True, horizontal_alignment='center')

    # Weather advisory (Heavy rainfall outlook)
    wahr = wxrow2.container(width=300)
    wahr.write('##### Weather advisory')
    wahr_content = wahr.container(height=200, border=True)

    # wahr_content.image('static/Clear.png')

    wahr_content.markdown(
        """
        <div style="display: flex; justify-content: center; align-items: center; height: 100%;">
            <img src="app/static/hvy rain outlook 2.png" width="145">
        </div>
        <div style="text-align: center; font-size: small;">
            as of 10:00 AM 31 August 2025
        </div>
        """,
        unsafe_allow_html=True
    )

    with wahr.expander('Details'):
        st.write(
            '''
            24-hour (daily) accumulated rainfall of 100-200 millimeters is possible today (September 1) until tomorrow aftenoon (September 2). Numerous flooding events are likely, especially in areas that are urbanized, low-lying, and/or near rivers or streams. Landslide likely in moderate to highly susceptible areas.
            '''
            )

I still see static/static in the code you just shared, I think that’s likely still the problem.

It works fine for me when running your app locally after changing that line, though there still seems to be a bug in the way you’re using st.markdown somewhere

My previous edits/commits apparently weren’t synced (I was editing via the GitHub mobile app, I’m now working on it through my PC on Codespaces). I applied the edits to all the containers in the “Weather” tab, replacing the ones using URLs to Wikimedia Commons to paths to their corresponding files in the static folder. For some reason, only the icon in the “Tropical cyclone winds” container became visible, while the others remain not visible.

I checked for double “/static” but there already none. I simply copy-pasted the markdown blocks and replaced the file name.

# Weather advisories & warnings
with weather_tab:
    # Setting row 1
    wxrow1 = st.container(horizontal=True, horizontal_alignment='center')
    
    # Thunderstorm
    tstm = wxrow1.container(width=300)
    tstm.write('##### Thunderstorm')
    
    tstm_content = tstm.container(height=200, border=True)

    
    tstm_content.markdown(
        """
        <div style="display: flex; justify-content: center; align-items: center; height: 100%;">
            <img src="app/static/rain warning - 00 (tstm adv).png" width="145">
        </div>
        <div style="text-align: center; font-size: small;">
            as of 10:00 AM 31 August 2025
        </div>
        """,
        unsafe_allow_html=True
    )

    # tstm_content = tstm.container(height=200, border=True, horizontal_alignment="center")
    # tstm_content.image("static/rain warning - 00 (tstm adv).png", width=145, caption="as of 10:00 AM 31 August 2025")

    with tstm.expander('Details'):
        st.write(
            '''
            Moderate to heavy rainshowers with possible isolated intense downpours, accompanied with lightning and strong winds, are ongoing or likely to occur within 1-2 hours.
            '''
            )
    
    # Rainfall
    rain = wxrow1.container(width=300)
    rain.write('##### Rainfall')
    rain_content = rain.container(height=200, border=True)
    
    rain_content.markdown(
        """
        <div style="display: flex; justify-content: center; align-items: center; height: 100%;">
            <img src="app/static/rain warning - 01 (rainfall advisory).png" width="145">
        </div>
        <div style="text-align: center; font-size: small;">
            as of 10:00 AM 31 August 2025
        </div>
        """,
        unsafe_allow_html=True
    )
    
    with rain.expander('Details'):
        st.write(
            '''
            Long-duration light to moderate rains with possible isolated heavy downpours are ongoing or likely to occur within 2-3 hours.
            '''
            )
    
    # Heavy rainfall
    heavyrainfall = wxrow1.container(width=300)
    heavyrainfall.write('##### Heavy rainfall')
    heavyrainfall_content = heavyrainfall.container(height=200, border=True)
    
    heavyrainfall_content.markdown(
        """
        <div style="display: flex; justify-content: center; align-items: center; height: 100%;">
            <img src="app/static/rain warning - 03 (orange hrwl).png" width="145">
        </div>
        <div style="text-align: center; font-size: small;">
            as of 10:00 AM 31 August 2025
        </div>
        """,
        unsafe_allow_html=True
    )
    
    with heavyrainfall.expander('Details'):
        st.write(
            '''
            Long-duration intense rainfall ongoing or likely to occur within 2-3 hours. Severe flooding is threatening to occur in areas that are urbanized, low-lying, and/or near rivers or streams. Landslides are likely to occur in hilly or mountainous areas.
            '''
            )
    
    # Setting row 2
    wxrow2 = st.container(horizontal=True, horizontal_alignment='center')

    # Weather advisory (Heavy rainfall outlook)
    wahr = wxrow2.container(width=300)
    wahr.write('##### Weather advisory')
    wahr_content = wahr.container(height=200, border=True)
    
    wahr_content.markdown(
        """
        <div style="display: flex; justify-content: center; align-items: center; height: 100%;">
            <img src="app/static/hvy rain outlook 2.png" width="145">
        </div>
        <div style="text-align: center; font-size: small;">
            as of 10:00 AM 31 August 2025
        </div>
        """,
        unsafe_allow_html=True
    )
    
    with wahr.expander('Details'):
        st.write(
            '''
            24-hour (daily) accumulated rainfall of 100-200 millimeters is possible today (September 1) until tomorrow aftenoon (September 2). Numerous flooding events are likely, especially in areas that are urbanized, low-lying, and/or near rivers or streams. Landslide likely in moderate to highly susceptible areas.
            '''
            )

    # Marine gale
    gale = wxrow2.container(width=300)
    gale.write('##### Marine gale')
    gale_content = gale.container(height=200, border=True)
    
    gale_content.markdown(
        """
        <div style="display: flex; justify-content: center; align-items: center; height: 100%;">
            <img src="app/static/gale warning - beaufort 8-9.png" width="145">
        </div>
        <div style="text-align: center; font-size: small;">
            as of 10:00 AM 31 August 2025
        </div>
        """,
        unsafe_allow_html=True
    )

    with gale.expander('Details'):
        st.write(
            '''
            Strong to gale-force winds are prevailing or expected, causing rough to very rough seas. Sea travel is risky for small seacrafts (including all motor bancas of any type or tonnage). Mariners of these vessels are advised to remain in port or seek safe harbor. For larger vessels, operating in these conditions require experience and properly equipped vessels
            '''
            )

    # Tropical cyclone wind signal
    tcws = wxrow2.container(width=300)
    tcws.write('##### Tropical cyclone winds')
    tcws_content = tcws.container(height=200, border=True)
    
    tcws_content.markdown(
        """
        <div style="display: flex; justify-content: center; align-items: center; height: 100%;">
            <img src="app/static/tcws2.png" width="145">
        </div>
        <div style="text-align: center; font-size: small;">
            as of 10:00 AM 31 August 2025
        </div>
        """,
        unsafe_allow_html=True
    )

    with tcws.expander('Details'):
        st.write(
            '''
            Gale-force winds are prevailing or expected to occur within 24 hours due to a tropical cyclone, posing minor to moderate threat to life and property.
            '''
            )

    # Setting row 3
    wxrow3 = st.container(horizontal=True, horizontal_alignment='center')

    # Storm surge
    stsurge = wxrow3.container(width=300)
    stsurge.write('##### Storm surge')
    stsurge_content = stsurge.container(height=200, border=True)
    
    stsurge_content.markdown(
        """
        <div style="display: flex; justify-content: center; align-items: center; height: 100%;">
            <img src="app/static/storm surge warning 2.png" width="145">
        </div>
        <div style="text-align: center; font-size: small;">
            as of 10:00 AM 31 August 2025
        </div>
        """,
        unsafe_allow_html=True
    )

    with stsurge.expander('Details'):
        st.write(
            '''
            There is a moderate to high risk of storm surge with heights of 2-3 meters within 24 hours due to a tropical cyclone. Severe damage is expected to communities and coastal/marine infrastructures, along with significant coastal erosion. Riverine flooding due to forced/induced upstream flow by the storm surge is also likely.
            '''
            )

Hello, I somehow figured out the problem. I noticed that the file name for the warning icon in the “Tropical cyclone winds” container had no whitespaces. I renamed the image file inside the “Thunderstorm” container, removing all whitespaces, like from “rain warning - 00 (tstm adv).png” to “rainwarning_00_tstmadv.png”. And it worked.

1 Like