Text diagrams in vanilla JavaScript, React, and Vue
Diagrams use the cell renderer directly. They do not need the 3D scene bindings.
Install @glyphcss/diagrams in your application, then pass Mermaid source or graph data.
The examples use color: "none" and assign the result as text.
Vanilla JavaScript
Section titled “Vanilla JavaScript”<pre id="diagram"></pre><style> #diagram { font-family: "Glyph Mono", ui-monospace, monospace; line-height: 1; overflow: auto; }</style>import { renderGlyphDiagram } from "@glyphcss/diagrams";
const host = document.querySelector<HTMLPreElement>("#diagram")!;try { const result = await renderGlyphDiagram(`flowchart LR Push[Push commit] --> Build[Build app] Build --> Test{Tests pass?} Test -->|yes| Stage[Deploy staging] Stage --> Smoke[Smoke tests] Smoke --> Prod[Deploy prod] Test -->|no| Fix[Fix build]`, { target: "web", color: "none", width: 72, height: 19, overflow: "expand", }); host.textContent = result.text;} catch (error) { host.textContent = error instanceof Error ? error.message : String(error);}import { useEffect, useState } from "react";import { renderGlyphDiagram } from "@glyphcss/diagrams";
export function GlyphOrderDiagram({ source = `flowchart LR Push[Push commit] --> Build[Build app] Build --> Test{Tests pass?} Test -->|yes| Stage[Deploy staging] Stage --> Smoke[Smoke tests] Smoke --> Prod[Deploy prod] Test -->|no| Fix[Fix build]` }) { const [text, setText] = useState(""); useEffect(() => { let active = true; renderGlyphDiagram(source, { target: "web", color: "none", width: 72, height: 19, overflow: "expand", }).then( result => { if (active) setText(result.text); }, error => { if (active) setText(error instanceof Error ? error.message : String(error)); }, ); return () => { active = false; }; }, [source]); return <pre className="order-diagram">{text}</pre>;}.order-diagram { font-family: "Glyph Mono", ui-monospace, monospace; line-height: 1; overflow: auto;}The cleanup prevents a completed older request from replacing a newer diagram or updating an unmounted component. It also tolerates React StrictMode replay.
<script setup lang="ts">import { ref, watchEffect } from "vue";import { renderGlyphDiagram } from "@glyphcss/diagrams";
const props = withDefaults(defineProps<{ source?: string }>(), { source: `flowchart LR Push[Push commit] --> Build[Build app] Build --> Test{Tests pass?} Test -->|yes| Stage[Deploy staging] Stage --> Smoke[Smoke tests] Smoke --> Prod[Deploy prod] Test -->|no| Fix[Fix build]`,});const text = ref("");watchEffect(onCleanup => { let active = true; onCleanup(() => { active = false; }); renderGlyphDiagram(props.source, { target: "web", color: "none", width: 72, height: 19, overflow: "expand", }).then( result => { if (active) text.value = result.text; }, error => { if (active) text.value = error instanceof Error ? error.message : String(error); }, );});</script>
<template><pre class="order-diagram">{{ text }}</pre></template>
<style scoped>.order-diagram { font-family: "Glyph Mono", ui-monospace, monospace; line-height: 1; overflow: auto;}</style>Rendered result
Section titled “Rendered result” ┌─────────────┐ ┌───────────┐ ╭───────────────╮
│ Push commit │────▶ Build app │────▶ ◆ Tests pass? │
└─────────────┘ └───────────┘ ╰───────────────╯
│ │
┌──────────────────────────────────┘ │ no
│ yes │
│ ┌──────▼────┐
│ │ Fix build │
│ └───────────┘
│
┌───────▼────────┐ ┌─────────────┐ ┌─────────────┐
│ Deploy staging │────▶ Smoke tests │────▶ Deploy prod │
└────────────────┘ └─────────────┘ └─────────────┘ Colored HTML
Section titled “Colored HTML”Use color: "css" to obtain result.html, escaped span markup for the contents of a <pre>.
Insert that fragment inside the <pre>; the library does not supply the outer element.
The library escapes label text; do not concatenate unescaped source into the fragment.
import { renderGlyphDiagram } from "@glyphcss/diagrams";
const result = await renderGlyphDiagram("flowchart LR; Build --> Ship", { target: "web", color: "css", width: 64, height: 16,});const wrapper = document.createElement("pre");wrapper.className = "diagram-html";wrapper.innerHTML = result.html!;document.body.append(wrapper);.diagram-html { font-family: "Glyph Mono", ui-monospace, monospace; line-height: 1; overflow: auto;}React can use <pre dangerouslySetInnerHTML={{ __html: html }} />; Vue can use
<pre v-html="html" />. Populate html from result.html and retain the same stale-result
cleanup as in the text examples. The pre needs the font and line-height rules above.
Resize and static rendering
Section titled “Resize and static rendering”Measure available columns and rows at a stable font size, then call the renderer again.
Use a ResizeObserver owned and disconnected by the component lifecycle. Read actual
expanded dimensions from result.canvas.grid; see sizing.
For a static site, call the renderer at build time and emit text or CSS HTML without hydration. These documentation examples do that through a shared Astro adapter; no diagram runtime is required to display their output. The same renderer works in Node for server output.