Skip to content

🤖 perf: lazy-load Mermaid diagrams - #6037

Merged
ThomasK33 merged 2 commits into
mainfrom
perf/t3-pr3-lazy-mermaid
Oct 10, 2026
Merged

ThomasK33 merged 2 commits into
mainfrom
perf/t3-pr3-lazy-mermaid

Conversation

@ThomasK33

Copy link
Copy Markdown
Member

Summary

Mermaid now loads only when a message or artifact contains a diagram (T3, Refs #5971). The mermaid core leaves the first load, so first-load JS drops by 470.6 KiB raw / 108.9 KiB br. While the chunk loads, a shared pending frame shows. It is the same frame Mermaid uses for its own pending state, so nothing shifts when the diagram arrives.

Background

Mermaid.tsx imports mermaid and calls mermaid.initialize at module top level. MarkdownComponents.tsx and ArtifactViewer.tsx imported it statically, so the mermaid core sat in the main chunk on every page load, even though diagrams are rare. The diagram-type chunks were already lazy.

Implementation

  • New MermaidPendingFrame.tsx: an eager module with no mermaid import. It holds the frame, container and pending styles and the "Rendering diagram..." text. Mermaid.tsx now uses these same style objects for its own pending state instead of inline copies. Its effects, debounce, render and sanitization are unchanged.
  • MarkdownComponents.tsx and ArtifactViewer.tsx each declare a module-top-level React.lazy for Mermaid and wrap it in <LazyFeature name="Diagram" fallback={<MermaidPendingFrame />}>. mermaid.initialize still runs when the chunk loads, before the first render.
  • The markdown hot path gains no hook, effect, subscription or per-delta work. LazyFeature is an ErrorBoundary around Suspense.
  • node_modules/mermaid/ is on FIRST_LOAD_FORBIDDEN_SOURCES. Commit 1 adds it with a test that fails on main: on main, 3 zoom buttons render synchronously where 0 are expected.

Validation

  • First-load bytes (make first-load-js, KiB). Both sides were clean make build runs with Bun 1.3.12. Base is the merge-base 9c1d222f47:
file base raw head raw Δ raw base br head br Δ br
main 7,081.9 6,611.2 -470.6 1,454.4 1,345.5 -108.9
API 448.8 448.8 0.0 130.6 130.5 -0.0
DesktopPanel 30.1 30.1 0.0 10.0 10.0 -0.0
TerminalRouterContext 18.4 18.4 0.0 6.5 6.5 0.0
coerce 0.2 0.2 0.0 0.2 0.2 0.0
total 7,579.4 7,108.7 -470.6 1,601.7 1,492.8 -108.9

mermaid is now only in Mermaid-*.js. The plan predicted 7,119 / 1,494 KiB.

  • Request log (Chrome, xum server on 127.0.0.1, first-run page and seeded workspace page). Both pages request the same JS files as base before LCP, and no new chunk at all. The text and the LCP element are the same as base.
  • Desktop cold start (D2, 20 pairs, base = merge-base, head = this PR): run https://github.com/coder/xum/actions/runs/38043401011: mean per-pair delta -3.15%, one-sided 95% upper bound -2.30% (gate: at most +5%), half-width 0.85%. Median xum:app-shell-ready: base 842.1 ms, head 811.0 ms.
  • Layout box (headless Chrome, seeded chat with a mermaid block, frame getBoundingClientRect): head: lazy fallback 714×332 px at top 200.7, Mermaid's own pending state 714×332 at 200.7, rendered SVG 714×332 at 200.7. Base: pending 714×332 at 200.7, SVG 714×332 at 200.7. No shift.
  • Remote UAT on Coder Agents passed on this head.
    • With XUM_MOCK_AI=1, a message with a mermaid block streams back. The diagram renders in both the user message and the reply, and the chunk is fetched only at the first diagram.
    • Switching A→B→A and a hard reload re-render every diagram.
    • A .mmd artifact renders in the Artifacts tab.
    • With the chunk held for 3 s, the fallback and Mermaid's own pending state had identical rects (0.00 px delta).
    • Stale chunk: every block shows "Diagram failed to load." with Reload, and the rest of the transcript and the composer keep working. Restoring the files and clicking Reload renders the diagrams.
  • make check-react-compiler still prints 23/24 hot components compile (1 known skipped) (MarkdownCore is on the hot list). make static-check passes.
  • Tests: bun test src/browser/features/Messages/ src/browser/features/RightSidebar/ArtifactsTab/ src/browser/components/LazyFeature/ ./scripts/perf/ gives 666 pass, 0 fail, including the markdown, streaming and Mermaid tests. Full tests/ui gives 357 passed, 3 failed. The 3 failures are creationGoalFormalRepro (2) and nameGeneration (1), which also fail on main (tests: 3 tests/ui tests fail on main (creationGoalFormalRepro, nameGeneration) #6016).

Risks

Low.

  • The first diagram in a session waits for one chunk (~120 KiB br) plus its diagram-type chunks, as before.
  • If the chunk fails to load (a stale tab after a server upgrade), each block shows a one-line "Diagram failed to load." with Reload instead of the 300 px frame.

Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high

…frame

The new Mermaid test fails until the markdown renderer lazy-loads Mermaid (Refs #5971).

Signed-off-by: Thomas Kosiewski <tk@coder.com>
MarkdownComponents and ArtifactViewer load Mermaid with React.lazy inside
LazyFeature, so the mermaid package leaves the first load (Refs #5971). The
fallback MermaidPendingFrame shares Mermaid's frame and pending styles, so the
box does not shift when the chunk arrives.

Signed-off-by: Thomas Kosiewski <tk@coder.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-10T10:17:12.621900Z 57c9656 PR opened
🔒 Security Review ✅ Completed 2026-10-10T10:18:22.410010Z 57c9656 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@ThomasK33

Copy link
Copy Markdown
Member Author

Dogfood evidence from remote UAT on 57c965695d, with XUM_MOCK_AI=1 streaming the mermaid block back.

  1. Lazy fallback while the Mermaid chunk is held:

Pending fallback

  1. Rendered in the user message and the streamed reply:

Rendered diagrams

  1. After switching back to workspace A:

Back on A

  1. Stale chunk: "Diagram failed to load." with Reload, while the rest of the transcript keeps working:

Stale chunk

  1. Mermaid artifact in the Artifacts tab:

Artifact

  1. Phone width (390 × 844):

390


Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high

@ThomasK33
ThomasK33 added this pull request to the merge queue Oct 10, 2026
Merged via the queue into main with commit 4d648e2 Oct 10, 2026
31 checks passed
@ThomasK33
ThomasK33 deleted the perf/t3-pr3-lazy-mermaid branch October 10, 2026 10:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant