pythonhtmlcssjinja2

SVG element not visible in streamlit


Below is my code with a circle inside svg element which i want to render through jinja2 using st.html in streamlit the issue is the continer is visible but svg element is not visible. it is blank. see below code.

<style>
html,body {
  height: 100%;
  background-color: #404040;
  margin: 0;
  display: flex;
  justify-content: center;
  align-items: center;
}

.container {
  display: flex;
  width: 500px;
  /* Extended width for pipe */
  height: 500px;
  border: 3px solid #EEEEEE;
  justify-content: center;
  align-items: center;
  border-radius: 20px;

}

.drawing {
  position: relative;
  width: 2000px;
  /* Extended width for pipe */
  height: 600px;
}

.circle-svg {
  width: 100%;
  height: 100%;
}

.c3 {
  fill: none;
  stroke: grey;
  stroke-width: 10;
}

</style>
<body>
  <div class="container">
    <div class="drawing">
      <svg viewBox="0 0 500 500" class="circle-svg">
                <!-- Circle -->
                <circle class="c3" cx="250" cy="250" r="198"></circle>
            </svg>
    </div>
  </div>
</body>

python code:

def cutting_dimention(html_content):
    # Directly use the HTML content without treating it as a file path
    template = Template(html_content)
    rendered_html = template.render()  # Render the HTML with Jinja2
    st.text(rendered_html)  # Display rendered HTML text
    st.html(rendered_html)  # Display rendered HTML as an HTML block in Streamlit

Call the function with the HTML content

cutting_dimention(my_circle)


Solution

  • I just had a play on Streamlit's playground page and noticed that st.html() will accept <p>A paragraph</p> but not your SVG tag.

    As documented, they do internal sanitizing with DOMPurify, who strips it out of your output because the <svg> tag is not in the list of accepted tags.

    I would suggest you two options:

    A. Create a component

    You could create your own Streamlit component, following the official documentation.

    B. Trick : use Markdown instead of HTML

    I finally Googled and found that the st.markdown() function has the parameter unsafe_allow_html which one can set to True, which will allow HTML in the Markdown code. And this output doesn't seem to be filtered with DOMPurify.

    Example:

    st.markdown(
        """
        <svg viewBox="0 0 500 500" class="circle-svg">
          <circle class="c3" cx="250" cy="250" r="198"></circle>
        </svg>""",
        unsafe_allow_html=True
    )
    

    Outputs:

    <div class="stElementContainer element-container st-emotion-cache-67qddt e1f1d6gn4" data-testid="stElementContainer" data-stale="false" width="252">
      <div class="stMarkdown" data-testid="stMarkdown" style="width: 252px;">
        <div data-testid="stMarkdownContainer" class="st-emotion-cache-phe2gf e1nzilvr5">
          <svg viewBox="0 0 500 500" class="circle-svg">
            <circle class="c3" cx="250" cy="250" r="198"></circle>
          </svg>
        </div>
      </div>
    </div>
    

    The HTML output with your SVG tag is wrapped in some <div> tags, and will not be put inside an <iframe>. So this might help you, as you'll be able to apply some CSS rules or attach some event handlers in JavaScript, without struggling to link the JS code between the iframe and the parent page, neither need to set a size to the iframe.

    Notice that the width of the element-container is updated live by the Streamlit JavaScript code. So the attributes width="252" and style="width: 252px;" are changed when you resize the page.

    This means that your SVG image will grow correctly by preserving its original ratio.

    C. Try to allow the <svg> tag

    I didn't see how to modify DOMPurify's config. It doesn't seem to be exposed by their API.

    So I asked Google Gemini:

    How do I configure streamlit to add the SVG tag as accepted tags in their internal DOMPurify configuration?

    It told me that one cannot modify the config directly as it is not given accessible via the API, but it also proposed to use the html component via st.components.v1.html(your_svg_code, height=110).

    I'm clearly not convinced, as it will put your SVG image inside an <iframe>, meaning that you won't be able to style it. Secondly, you'll have to set a height to this iframe, which is stupid in your case.

    Finally, what to choose?

    I don't really know what is the best practise, but I would go for option A, to build a component that I can re-use or share with other Streamlit users.

    I would choose option B if I quickly have to get things working and don't need to build a re-usable component.