A Diagram Renderer of your own

A Diagram Renderer turns the source text of one format into an SVG string. The editor shows the result as an image, so its SVG never runs script in the page.
Pro ExtensionAlphaPilot program

Quick start

This one sends Mermaid source to a rendering service you host and returns what it answers:

import type { DiagramRenderer } from '@tiptap-pro/extension-diagrams'

export function serviceDiagramRenderer(endpoint: string): DiagramRenderer {
  return {
    id: 'mermaid-service',
    label: 'Mermaid (service)',
    formats: ['mermaid'],
    async render({ source, theme, raster }) {
      const response = await fetch(endpoint, {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ source, theme, htmlLabels: !raster }),
      })
      if (!response.ok) throw new Error(`The diagram service answered ${response.status}`)
      const svg = await response.text()
      const root = new DOMParser().parseFromString(svg, 'image/svg+xml').documentElement
      const width = Number.parseFloat(root.getAttribute('width') ?? '') || 600
      const height = Number.parseFloat(root.getAttribute('height') ?? '') || 400
      return { svg, width, height }
    },
  }
}
Diagrams.configure({
  diagramRenderers: [serviceDiagramRenderer('https://diagrams.example.com/mermaid/svg')],
})

Members

A Diagram Renderer is { id, label?, formats, draws?, render, editing? }.

MemberTypeDescription
idstringNames the Renderer
labelstringOptional. For a menu
formatsreadonly FlowDiagramFormat[]The diagram formats it draws
draws(source: string) => booleanOptional. Whether it draws this source, such as only some Mermaid kinds. Without it, every source
render(input) => Promise<{ svg, width, height }>Draws the source, described below
editingDiagramEditingOptional. Finds text and shapes in its pictures, see Editing in the picture

Which Renderer draws a diagram

  • The first registered Diagram Renderer whose formats include the diagram's format, and whose draws does not decline the source, draws the diagram.
  • A kind no Renderer draws shows what unavailable returns, with the reason 'unsupported-kind'. The source stays in the document.
  • A format of your own needs no other registration. See Other formats.

What render receives

InputTypeDescription
formatFlowDiagramFormatmermaid, drawio, bpmn or a format of your own, one of the Renderer's formats
sourcestringThe diagram source text, as the document stores it
themeDiagramThemeResolved colors and fonts, read from the --tt-flow-diagram-* custom properties (Diagrams)
rasterbooleantrue when the SVG becomes a PNG, a WebP, a JPEG or a picture in Word

When raster is true, labels must be SVG text: the SVG must hold no foreignObject, which the browser does not let a canvas export.

What render returns

ResultTypeDescription
svgstringA standalone SVG string with a width and a height
width, heightnumberThe size of the drawing in pixels

Editing in the picture

A double click on a text and dragging a shape need to know where things are in the picture. Only the Renderer knows its markup, so it says so through editing. Every function receives the SVG it returned, as a string, and the diagram's source.

MemberSignatureWithout it
labelAt(svg, point, source) => DiagramLabelHit | nullA double click opens nothing
nodeAt(svg, point, source) => DiagramNodeHit | nullNo shape can be dragged
movePositions(svg, positions, source) => string | nullNo shape can be dragged; stored offsets are not drawn
  • point is in the SVG's own units, through its viewBox.
  • DiagramLabelHit is { target, label, box }. target names the text in the source, such as { kind: 'node', id: 'A' }, and the extension writes the new text there.
  • DiagramNodeHit is { id, label, box }, id as the source names the shape.
  • movePositions returns the picture with each shape moved by its offset, null when nothing moved.
  • Return null for a kind your Renderer does not handle.

A Renderer that draws with mermaid.js reuses the built-in one:

import { mermaidDiagramEditing } from '@tiptap-pro/extension-diagrams/mermaid'

const renderer = { id: 'my-mermaid', formats: ['mermaid'], render, editing: mermaidDiagramEditing }

Keeping the page safe

  • The SVG is shown in an <img>, so scripts and outside resources in it never run or load in the editor.
  • The source must not be evaluated in the page. Render it on a server or in a sandboxed iframe, as the built-in Renderers do.
  • A library that only parses text and needs no DOM can draw in a Web Worker instead, as the Pele Renderer does.