St.markdown does not render mermaid graphs

Is there a way to ensure that mermaid annotations in markdown can be rendered as graphs using st.markdown?

Currently it only renders as text and does not convert to the graphical view

1 Like

Hi @Gireesh_Ramji, welcome to the Streamlit community!

Please note that mermaid.js may be using Markdown-like syntax, but it’s not part of the actual Markdown specification. Given the unbounded number of libraries that might have Markdown-like syntax, it isn’t feasible for Streamlit to include all of them in the base distribution.

This would be a great candidate for a custom component, though it would likely take some understanding of JavaScript first.

Best,
Randy

3 Likes

Thanks @randyzwitch, cool we will look into building a custom component for this!

For anyone willing to have mermaid graph in Streamlit, here is a simple way to do it using a Custom component:

def mermaid(code: str) -> None:
    components.html(
        f"""
        <pre class="mermaid">
            {code}
        </pre>

        <script type="module">
            import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
            mermaid.initialize({{ startOnLoad: true }});
        </script>
        """
    )


mermaid(
    """
    graph LR
        A --> B --> C
    """
)

The result is:

image

See Mermaid Usage for more details.

6 Likes

Thanks, this looks like what we need!

i tried using my own mermaid js code in that component and it partially rendered it instead of rendering the whole thing , here is my input
graph TB
A(Start)
A → B[Revocation of Previous Wills and Codicils]
B → C[Payment of Debts and Expenses]
C → D[Distribution of Residuary Estate]
D → E{Is Katia alive?}
E → |Yes| F[Allocate 60% to Katia]
E → |No| G{Is Mauro alive?}
G → |Yes| H[Allocate 40% to Mauro]
G → |No| I[Allocate to Living Issue per Stirpes or Survivor]
D → J[Appointment of Executor]
J → K{Is Katia able to serve?}
K → |Yes| L[Katia as Executor]
K → |No| M[Andrea and Paolo as Successor Executors]
L → N[Executor’s Power to Appoint Co-Executor]
M → N
N → O(End)

@tarek-kerbedj components.html accepts a height which will allow you to make it larger to see the whole diagram.

1 Like

that’s great but, how since in my case i wouldn’t know the size of the graph that would be generated beforehand i won’t be able to change it manually every time, is there a way to make sure that it dynamically fits?
also on a related note, can i increase the size of the cells/ font size of the mermaid graph
sorry for the noobie questions as i’m completely unfamiliar with js

@tarek-kerbedj This is a bit convoluted, but you can do it (with a little delay) by using javascript to get the height of the svg that mermaid generates:

Sorry for the late reply , Thank you ! the solution does seem to work but is the second part of my question possible , how can i increase the size of the graph , more specifically the text in the cells

Yes, that can be changed by changing the default style, like this

1 Like

This is really helpful, thank you! Does anyone have a straight-forward way of converting mermaid to an image that can be downloaded by the user? From what I have researched it seems that puppeteer or another simulation-like JS tool is needed but I’d be very keen to know if there is a simpler way!

I am also looking for a more straightforward approach.
In my case, I have several mermaid blocks mixed with markdown ones.

I am currently using the streamlit-mermaid lib, however it wont deal with the height issue. The code suggested above also wont deal with multiple blocks.

This is the function that I ended up using for now, to deal with multiple charts

function adjustIframeHeight(iframe) {
    try {
        var iframeDoc = iframe.contentDocument;
        var mermaidElement = iframeDoc.getElementsByClassName("mermaid")[0];
        if (mermaidElement) {
            var svgElement = mermaidElement.getElementsByTagName("svg")[0];
            if (svgElement) {
                var bbox = svgElement.getBBox();
                iframe.style.height = (bbox.height + 50) + "px";
            }
        }
    } catch (e) {
        console.error("Error adjusting iframe height: ", e);
    }
}

function observeIframe(iframe) {
    var observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            adjustIframeHeight(iframe);
        });
    });

    try {
        var config = { attributes: true, childList: true, subtree: true };
        observer.observe(iframe.contentDocument, config);
    } catch (e) {
        console.error("Error observing iframe: ", e);
    }
}

var iframes = parent.document.getElementsByTagName("iframe");
for (var i = 0; i < iframes.length; i++) {
    observeIframe(iframes[i]);
}

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.