---
title: "A Diagram Renderer of your own"
description: "A Diagram Renderer turns the source text of one format into an SVG string."
canonical_url: "https://tiptap.dev/docs/diagrams/renderers/custom-diagrams"
---

# A Diagram Renderer of your own

A Diagram Renderer turns the source text of one format into an SVG string.

## Quick start

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

```ts
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 }
    },
  }
}
```

```ts
Diagrams.configure({
  diagramRenderers: [serviceDiagramRenderer('https://diagrams.example.com/mermaid/svg')],
})
```

## Members

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

| Member    | Type                                         | Description                                                                                                                                                         |
| --------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`      | `string`                                     | Names the Renderer                                                                                                                                                  |
| `label`   | `string`                                     | Optional. For a menu                                                                                                                                                |
| `formats` | `readonly FlowDiagramFormat[]`               | The diagram formats it draws                                                                                                                                        |
| `draws`   | `(source: string) => boolean`                | Optional. 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                                                                                                                                   |
| `editing` | `DiagramEditing`                             | Optional. Finds text and shapes in its pictures, see [Editing in the picture](https://tiptap.dev/docs/diagrams/renderers/custom-diagrams.md#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`](https://tiptap.dev/docs/diagrams/api-reference/options.md#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](https://tiptap.dev/docs/diagrams/flow-diagrams/formats.md#other-formats).

## What render receives

| Input    | Type                | Description                                                                                                                                                         |
| -------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `format` | `FlowDiagramFormat` | `mermaid`, `drawio`, `bpmn` or a format of your own, one of the Renderer's `formats`                                                                                |
| `source` | `string`            | The diagram source text, as the document stores it                                                                                                                  |
| `theme`  | `DiagramTheme`      | Resolved colors and fonts, read from the `--tt-flow-diagram-*` custom properties ([Diagrams](https://tiptap.dev/docs/diagrams/api-reference/selectors.md#diagrams)) |
| `raster` | `boolean`           | `true` 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

| Result            | Type     | Description                                           |
| ----------------- | -------- | ----------------------------------------------------- |
| `svg`             | `string` | A standalone SVG string with a `width` and a `height` |
| `width`, `height` | `number` | The 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.

| Member          | Signature                                         | Without it                                            |
| --------------- | ------------------------------------------------- | ----------------------------------------------------- |
| `labelAt`       | `(svg, point, source) => DiagramLabelHit \| null` | A double click opens nothing                          |
| `nodeAt`        | `(svg, point, source) => DiagramNodeHit \| null`  | No shape can be dragged                               |
| `movePositions` | `(svg, positions, source) => string \| null`      | No 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:

```ts
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.
