Are you using HTML in Markdown? Tell us why!

I use st.markdown with unsafe_allow_html to inject custom CSS and Google Material Design icons.


Hi Isaac,

Thanks for your note.

Could you please help me with a sample ?


Yeah, sure. Here is an example of loading custom CSS.

st.markdown('<style>h1{color: red;}</style>', unsafe_allow_html=True)

And here is an example of loading a custom icon after I’ve included the icon CSS library using the hack above.

st.markdown('<i class="material-icons">face</i>', unsafe_allow_html=True)

More direct support for custom css using like st.css(css: str | dict) and custom icons using like st.icon('face') would be amazing.


Thanks a lot Isaac. You saved my day.

Hi Isaac,

Sorry to disturb you again. Could you please help me with how to import material-icons as you mentioned in the following line?
st.markdown(‘face’, unsafe_allow_html=True)

  1. Download the Google Material Design CSS stylesheet from locally to e.g. icons.css.
  2. Load css using hack I specified above.
st.markdown('<style>' + open('icons.css').read() + '</style>', unsafe_allow_html=True)
  1. Display icon by injecting icon html.
st.markdown('<i class="material-icons">face</i>', unsafe_allow_html=True)

But it would be awesome if the maintainers could add st.css() and st.icon() features.


That’s a cool idea, but we’ll have to think a bit about how it fits with the rest of the API. For example, should icons be clickable? How would st.css interop with our planned horizontal/grid layout APIs? etc.

In the meantime, you could always create your own library and share with the community through Github :smiley:

Something like:

(NOTE: This code is untested!)

import streamlit as st

def local_css(file_name):
    with open(file_name) as f:
        st.markdown('<style>{}</style>'.format(, unsafe_allow_html=True)

def remote_css(url):
    st.markdown('<style src="{}"></style>'.format(url), unsafe_allow_html=True)

def icon_css(icone_name):

def icon(icon_name):
    st.markdown('<i class="material-icons">{}</i>'.format(icon_name), unsafe_allow_html=True)

EDIT: Found the static html folder, I’ll just symlink my generated htmls inside it for the time being. It’s a bit of a sketchy workaround, but it’s okay for now![/quote]

Nice hack workaround! You could also copy your JS/CSS into that folder from within Python, so anyone running your app would get the hack for free.

For that, you’d have to find the root folder for Streamlit by using os.path.dirname(st.__file__)

Yup!! We’re planning on supporting that via easy-to-build plugins:

Nice hack workaround! You could also copy your JS/CSS into that folder from within Python, so anyone running your app would get the hack for free.
For that, you’d have to find the root folder for Streamlit by using os.path.dirname(st.__file__)

Wow, that is all that was needed to make the hack perfect! Thank you, and I’m still looking forward to the plugins :slight_smile:
Have a nice day!

1 Like

1.Currently I find folium -maps much better than deck_gl_chart. So therefore I use for example:

import streamlit as st
import pandas as pd
import folium

all_cities = pd.DataFrame(
    [(40.7127837, -74.0059413),(34.0522342, -118.2436849),(41.8781136, -87.6297982)],
    ['New York', 'Los Angeles', 'Chicago'],

selected_city = st.selectbox('Select city', all_cities.index)

route_map = folium.Map(location=(all_cities.loc[selected_city]), zoom_start=9, control_scale=True)

    .Marker(all_cities.loc[selected_city], popup='<i>'+selected_city+'</i>', tooltip='Where am I?')\

st.markdown(route_map._repr_html_(), unsafe_allow_html=True)
  1. Maybe you could add st.folium to the API?

Hi Thiago,

Thanks for working on Streamlit. It’s a great tool. I definitely hear your concerns about how would st.css play nicely with the planned grid layout API, but I still think there is a use-case for custom user-defined css. For a specific example, how else do I color/size my titles and links to match my product branding? Even if we could pass in custom options to those fields, I would still prefer to have the full low-level control enabled by writing my own css which takes precedence over the streamlit-default css. My hope is that you don’t deprecate the st.markdown(..., unsafe_allow_html: True) option before we have another way to accomplish that.



The documentation still says “Also note that unsafe_allow_html is a temporary measure and may be removed from Streamlit at any time.”

Has this decision been made yet? I think it’s important to keep ‘allow_html’.

I would like to change text direction to right to left ‘RTL’. Does Streamlit markdown support something like that?


Awesome! I was wondering if I am the only one using folium. I am wondering could you change the size of map box. Thanks a lot

Hi @Yang_Zhang ,

Check here:

You can pass width and height either as a pixel int or percentage string (default: ‘100%’).

1 Like

Thank you~

I have user texts in my data that may or may not contain line breaks, sometimes two in a row. It seems that the markdown processor does something strange when this happens. Only the first paragraph is enclosed in the HTML tags:

st.markdown(f'<div style="font-size: small">{document}</div>',unsafe_allow_html=True)

renders to

<div class="markdown-text-container stText" style="width: 698px;">
  <div style="font-size: small;">This is paragraph 1 text.</div>
  <p>This is paragraph 2 text.</p>
  <p>This is paragraph 3 text.</p>

All I really want is some way to have the text stand out, be word-wrapped, and respect line breaks in the original text. I wanted to use markdown blockquote but the prepending a “>” to the document only blockquotes the first paragraph. What’s the best way to do this?

1 Like

Created a whole issue on this topic - Using streamlit for an STT / TTS model demo?

I wonder if I should create a feature request?

I do, because of <sub>subscriptsts</sub>

1 Like

I’m curious what your application for directly specifying RTL would be. I’ve been able to create text in RTL languages using st.write without any special markup around them, relying on browser detection only.

If you mean you are trying to reverse an LTR language, I’m not sure there’s a solution for that even in HTML. :slight_smile:

Since this is a thread asking “what are you using HTML for”, I’m wondering how far you can get in Streamlit without using HTML for what you are doing @mzeidhassan.

1 Like

Hey @arminp,

As a workaround for file download support, here’s a Gist that demonstrates patching Streamlit to allow static file downloads: