Custom Rendering

Use a custom renderer only when you want to own the whole diagram experience. If you only need surrounding layout, shared loading UI, or an error presentation, wrap <Mermaid> instead and retain the built-in renderer.

Understand renderer selection

Set components.renderer to the name of a component in ~/components:

export default defineNuxtConfig({
  contentMermaid: {
    components: {
      renderer: 'CustomMermaid',
      spinner: 'MermaidSpinner',
    },
  },
})

The configured name is a Custom Renderer Candidate. On the client, the package searches ~/components recursively for a matching filename; matching ignores case, spaces, underscores, hyphens, path segments, and an optional .vue suffix.

Resolution outcomeRendering owner
No renderer nameBuilt-in Renderer.
Candidate is loadingNo renderer yet; the default source fallback remains visible.
Candidate resolvesCustom Renderer, exclusively.
Candidate is not found or fails to loadBuilt-in Renderer after a console diagnostic.

This is a one-way ownership choice per resolved candidate. A Custom Renderer that later fails to mount or render does not cause a second built-in fallback. Its loading, error UI, theme decisions, Mermaid invocation, toolbar, fullscreen, expand, and accessibility behavior are yours to implement.

Implement a complete custom renderer

A resolved Custom Renderer receives exactly these extension inputs:

  • code: decoded Mermaid source, when the caller passed it.
  • spinner: the configured spinner component, or the package spinner fallback.
  • The default slot: the source fallback supplied by <Mermaid>.

It does not receive built-in pageConfig, direct config, toolbar options, loading slots, error slots, automatic theme resolution, or components.error. Own those concerns explicitly:

<!-- components/CustomMermaid.vue -->
<script setup lang="ts">
import { onMounted, ref, shallowRef, useId } from 'vue'
import type { Component } from 'vue'

const props = defineProps<{
  code?: string
  spinner: Component | string
}>()

const loading = ref(true)
const error = shallowRef<unknown>()
const svg = ref('')
const renderId = `custom-mermaid-${useId().replaceAll(':', '')}`

onMounted(async () => {
  try {
    const mermaid = await useNuxtApp().$mermaid()
    svg.value = (await mermaid.render(renderId, props.code ?? '')).svg
  }
  catch (cause) {
    error.value = cause
  }
  finally {
    loading.value = false
  }
})
</script>

<template>
  <section class="custom-mermaid">
    <component :is="props.spinner" v-if="loading" />
    <p v-else-if="error" role="alert">
      Diagram failed: {{ error instanceof Error ? error.message : String(error) }}
    </p>
    <div v-else v-html="svg" />
  </section>
</template>

The example deliberately owns failure rendering. If your renderer needs theme changes or Mermaid configuration, provide and react to its own application-level state rather than expecting the built-in renderer's props.

Wrap the built-in renderer instead

Do not configure this wrapper as components.renderer. It is an ordinary application component that preserves built-in configuration, interactions, loading, and error behavior while adding presentation:

<!-- components/DiagramCard.vue -->
<script setup lang="ts">
const props = defineProps<{
  title: string
  source: string
}>()

const encodedSource = computed(() => encodeURIComponent(props.source))
</script>

<template>
  <section class="diagram-card">
    <h2>{{ title }}</h2>
    <Mermaid :code="encodedSource">
      <template #loading>
        <p aria-live="polite">Rendering diagram…</p>
      </template>
      <template #error="{ error, source }">
        <p role="alert">
          Render failed: {{ error instanceof Error ? error.message : String(error) }}
        </p>
        <details>
          <summary>Show Mermaid source</summary>
          <pre><code>{{ source }}</code></pre>
        </details>
      </template>
    </Mermaid>
  </section>
</template>

Add import { computed } from 'vue' if your Nuxt setup does not auto-import Vue utilities. The built-in renderer accepts a default slot for source fallback, #loading for its initial loading state, and #error="{ error, source }" for a Mermaid render failure. Alternatively, components.error supplies a global built-in error component and receives error and source; it is not used by a Custom Renderer.

Avoid recursive renderer selection

Never render <Mermaid> inside the component configured by components.renderer. The nested component repeats candidate selection and can recurse indefinitely. Call useNuxtApp().$mermaid(), another renderer, or a separate lower-level implementation instead. A wrapper is safe only when it is not the configured custom renderer.