A Diagram Renderer of your own

A Diagram Renderer turns a diagram's source text into an SVG string, for one or more diagram formats such as Mermaid. The editor shows the SVG as an image.

Quick start

This Diagram Renderer sends Mermaid source to a rendering service you host:

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

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
rulesRendererRulesOptional. See Rules of your Renderer
render(input) => Promise<{ svg, width, height }>Draws the source, described below
editingDiagramEditingOptional. Finds texts and shapes in its SVG, see Editing in the diagram

Which Renderer draws a diagram

The extension picks the first registered Diagram Renderer whose formats include the diagram's format and whose draws does not decline the source. Empty source goes to the first Diagram Renderer of its format, without asking draws.

A kind no Renderer draws shows what unavailable returns, with the reason 'unsupported-kind'. The source stays in the document. Unlike a chart Renderer, a declining Diagram Renderer never draws the diagram after all.

A format of your own

insertFlowDiagram and updateFlowDiagram accept a format while a registered Diagram Renderer lists it in formats. It needs no other registration.

  • The format's id has letters, digits, _ and -, up to 64 characters.
  • The source is stored as it is, up to 1,000,000 characters.
  • A format no registered Diagram Renderer draws stays in the document unchanged and survives every save. The diagram shows what unavailable returns.

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 an image in Word

When raster is true, write labels as SVG text. The SVG must hold no foreignObject, because the browser does not let a canvas export one.

What render returns

render returns { svg, width, height }. svg is a standalone SVG string with a width and a height. width and height are numbers, the size of the drawing in pixels.

Editing in the diagram

A user can double click a text in a diagram to edit it, and drag a shape to move it. See Editing in the diagram. Only the Renderer knows where texts and shapes are in its SVG, so it tells the extension through editing. Every function of editing receives the SVG that render 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, but stay in the document
  • 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' }. The extension writes the new text there.
  • DiagramNodeHit is { id, label, box }. id is the shape's id in the source.
  • movePositions returns the SVG with each shape moved by its offset, or null when nothing moved.
  • Return null for a diagram kind your Renderer does not handle.

A Renderer that draws with mermaid.js can reuse the built-in editing:

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

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

Keeping your web page safe

The editor shows the SVG in an <img>, so scripts and outside resources in the SVG never run or load in your web page.

Never evaluate the source in your web 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.