Are you using HTML in Markdown? Tell us why!

As described in this Github issue, it is very easy to write unsafe apps when developers have the ability to code in HTML. The idea is that users should always be able to trust Streamlit apps. So we have turned HTML off by default in Streamlit and will be removing the option to turn it on at some point in the future. (Update: We heard you, and have no plans to deprecate allow_unsafe_html! :pray:)

However, there are some legitimate use cases for writing HTML today to get around limitations in Streamlitā€™s current feature set. Some examples are:

  • Layout: laying items side by side, changing margin/padding/alignment properties, at.
  • Text-based diagrams: see this example from a user working on NLP.

(Rest assured, we are currently working on solutions to the use-cases above. You will hear about them very soon!)

If youā€™re using HTML in Streamlit, we would like to hear from you! Let us know:

  1. Why are you using it?
  2. Any thoughts on what youā€™d like to see in Streamlit if you did not have the ability to use HTML?

Thanks!

5 Likes

I am working in NLP and want to highlight passages of text in different ways.
E.g. highlight negated words in red.
Afaik is this not possible in markdown and when introducing custom HTML elements as text they get moved into their own span objects even with allow_unsave_html turned on.
It is possible to do this, we just canā€™t use quotes or style tags in the element. The solution was to also print the css in tags and avoid styling individual elements.

2 Likes

Thanks for the info. This is really helpful! :pray:

Are you trying to put colored boxes around sections of a sentence like this? (See the ā€œNamed entitiesā€ part of that Streamlit app)

If so, weā€™re considering different solutions for this use case right now. Will let you know when we have a more concrete proposal to share!


Also, it looks like you already solved your problem, but in case it helps hereā€™s a Gist showing Ines Montaniā€™s app above was done.

2 Likes

I am using it to create rich tables (e.g. with images in them, or other charts). Not sure if there is a better way to go about it.

1 Like

Hi Streamlit team! First, thank you very much for creating Streamlit. I canā€™t begin to express how much this helps with my day-to-day challenges. I have completely moved off of my custom Flask/React tool that I created to manage my workflow.

My current area of focus is OCR from highly quantized images. My models require copious amount of tuning and adjustments along with combinations of multiple feature extractors to work. To this end, when Iā€™m processing my image or video sources, I need to be able to see how all the feature extractions work side-by-side. My solution within Streamlit was to create a Dataframe with embedded PIL Images, that I use a custom formatter on. I got the idea from here:

https://www.kaggle.com/stassl/displaying-inline-images-in-pandas-dataframe

I would prefer to be able to simply use the Pandas Styler, but unfortunately their format does not allow the embedding of HTML currently, though it is mentioned as a possible future enhancement.

The other way I could work around this is by creating a custom Jinja2 template the Styler uses and repurpose the current Pandas html.tpl, see here:

This would in my opinion be a complete abuse of the Styler as I would have to override the displaying of the table elements in the TR block, with the undocumented r & c values which could easily be renamed in the future.

Here is a sample of the code I use to display images within a Pandas table:

def get_thumbnail(path):
  i = Image.open(path)
  i.thumbnail((150, 150), Image.LANCZOS)
  return i

def image_base64(im):
  base64Image = None
  if isinstance(im, str):
    im = get_thumbnail(im)
  with BytesIO() as buffer:
    im.save(buffer, 'jpeg')
    base64Image = base64.b64encode(buffer.getvalue()).decode()
  return base64Image

def image_formatter(im):
  return f'<img src="data:image/jpeg;base64,{image_base64(im)}">'

def run_the_app():
...
  df = pd.DataFrame(data)
  st.markdown(df.to_html(formatters={'image': image_formatter}, escape=False), unsafe_allow_html=True)
...

As you can see I perform a to_html() on the DataFrame with a custom formatter, and then pass the result to st.markdown. While this solution meets my needs it is not ideal, as I cannot leverage the Pandas Styler as its render method will not allow disabling of escaping HTML tags.

I hope this helps better understand my personal need of HTML. If I may make a recommendation, remove html from markdown and make st.html() a first-class citizen. Or make PIL Images a valid type in Streamlit dataframes which get rendered in the browser.

2 Likes

Thanks for the info @tbatchelli and @cajones! Super useful.

Based on what youā€™re saying, I think when we add support for horizontal layouts we should probably also support grid layouts. So I created a feature request here: https://github.com/streamlit/streamlit/issues/309 .

Please follow the feature there to be notified when we implement it. And help us come up with a nice API for it too!

5 Likes

My use case is embedding youtube videos

There already is a video widget. It would be cool to be able to give it a youtube link.

3 Likes

Great idea. Feature request created: Ability to pass URLs (and YouTube link!) to st.video Ā· Issue #348 Ā· streamlit/streamlit Ā· GitHub

For now i just do

def video_youtube(src: str, width="100%", height=315):
    """An extension of the video widget
    Arguments:
        src {str} -- A youtube url like https://www.youtube.com/embed/B2iAodr0fOo
    Keyword Arguments:
        width {str} -- The width of the video (default: {"100%"})
        height {int} -- The height of the video (default: {315})
    """
    st.write(
        f'<iframe width="{width}" height="{height}" src="{src}" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>',
        unsafe_allow_html=True,
    )

I guess I will be developing more extensions using html as I continue. You can find them in for inspiration

1 Like

I would like to create a tooltip to provide more info related to an item on my app. So, once a user hovers over it, the tooltip should be displayed. Is there a way to achieve this at the moment?

One more question that is related to HTML:
Is there a way to customize the HTML of the default index page and add custom CSS styling as well, etc.?
Thanks for the amazing app. Itā€™s really amazing, and speeds up everything. Thanks a million!

3 Likes

@jeremieā€™s usecase is embedding SVGs in Streamlit.

Hi. The use case that came to my mind (and actually how I found this page) is embedding an interactive code/text editor like this one: https://ace.c9.io/

Iā€™m using it to add <hr> in the sidebar, to separate groups of related widgets:

st.sidebar.markdown("<hr>", unsafe_allow_html=True)
1 Like

<hr> is supported in Streamlit markdown with ---. So you could do something like this:


import streamlit as st

st.sidebar.subheader("Component 1")

t1 = st.sidebar.text_input("Component 1 name")
s1 = st.sidebar.slider("Component 1 value")

st.sidebar.markdown("---")

st.sidebar.subheader("Component 2")
t2 = st.sidebar.text_input("Component 2 name")
s2 = st.sidebar.slider("Component 2")

st.title("Hello!")

st.write(t1, s1)
st.write(t2, s2)

5 Likes

I would like to embed graph visualisations based on d3js (e.g. based on this library https://github.com/vasturiano/force-graph) in my streamlit app. Is this even possible?

Hi @arminp

Thanks for the info! Iā€™m actually not even sure youā€™ll be able to make that work with the limited HTML that is supported in Markdown.

Our long term solution for situations like yours will be an easy-to-use plug-in architecture. You can follow the feature request here: https://github.com/streamlit/streamlit/issues/327

Thanks @thiago for the fast response! Iā€™ll track the feature request :slight_smile:

I am still wondering about a temporary workaround. So far, I need to run a separate local python server to access the visualisation htmls (because of the browserā€™s same-origin policy). Would it be possible for me to copy the vis-htmls to the local streamlit folder, in order to access them from the same port as streamlit (8501 for instance)?

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! Being able to embed js in streamlit at some point would be awesome!

Hello,
Iā€™m using the allow html option to display Spacy NER modelā€™s output, hereā€™s my code :

doc = nlp(text)
html_page = displacy.render([doc], style=ā€œentā€, page=True)
st.write(html_page,unsafe_allow_html=True)

2 Likes

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

4 Likes

Hi Isaac,

Thanks for your note.

Could you please help me with a sample ?

Thanks
Iyyappan