diff --git a/.changeset/mermaid-stable-svg-html.md b/.changeset/mermaid-stable-svg-html.md
new file mode 100644
index 00000000..bd1a62b0
--- /dev/null
+++ b/.changeset/mermaid-stable-svg-html.md
@@ -0,0 +1,5 @@
+---
+"streamdown": patch
+---
+
+perf: stop re-parsing a Mermaid diagram's SVG every time it re-renders with the same chart
diff --git a/packages/streamdown/__tests__/mermaid-component.test.tsx b/packages/streamdown/__tests__/mermaid-component.test.tsx
index d8f90953..c6de6e8b 100644
--- a/packages/streamdown/__tests__/mermaid-component.test.tsx
+++ b/packages/streamdown/__tests__/mermaid-component.test.tsx
@@ -247,6 +247,21 @@ describe("Mermaid Component", () => {
});
});
+ it("should keep the rendered SVG node when re-rendering the same chart", async () => {
+ const { container, rerender } = renderWithContext(
+
+ );
+
+ await waitFor(() => {
+ expect(container.querySelector('[role="img"] svg')).toBeTruthy();
+ });
+ const svg = container.querySelector('[role="img"] svg');
+
+ rerender();
+
+ expect(container.querySelector('[role="img"] svg')).toBe(svg);
+ });
+
it("should generate unique IDs for multiple charts", async () => {
const mockRender = vi.fn().mockResolvedValue({
svg: "",
diff --git a/packages/streamdown/lib/mermaid/index.tsx b/packages/streamdown/lib/mermaid/index.tsx
index d86d350f..6fc5d5a7 100644
--- a/packages/streamdown/lib/mermaid/index.tsx
+++ b/packages/streamdown/lib/mermaid/index.tsx
@@ -1,4 +1,4 @@
-import { useContext, useEffect, useRef, useState } from "react";
+import { useContext, useEffect, useMemo, useRef, useState } from "react";
import { useDeferredRender } from "../../hooks/use-deferred-render";
import { StreamdownContext } from "../../index";
import { useIsCodeFenceIncomplete } from "../block-incomplete-context";
@@ -210,6 +210,11 @@ export const Mermaid = ({
renderLatest();
}, [chart, config, retryCount, shouldRender, mermaidPlugin]);
+ // Always render the SVG if we have content (either current or last valid)
+ const displaySvg = svgContent || lastValidSvg;
+ // react-dom 19.2 rewrites innerHTML whenever this object's identity changes
+ const svgHtml = useMemo(() => ({ __html: displaySvg }), [displaySvg]);
+
// Show placeholder when not scheduled to render
if (!(shouldRender || svgContent || lastValidSvg)) {
return (
@@ -275,9 +280,6 @@ export const Mermaid = ({
);
}
- // Always render the SVG if we have content (either current or last valid)
- const displaySvg = svgContent || lastValidSvg;
-
return (