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 outcome | Rendering owner |
|---|---|
| No renderer name | Built-in Renderer. |
| Candidate is loading | No renderer yet; the default source fallback remains visible. |
| Candidate resolves | Custom Renderer, exclusively. |
| Candidate is not found or fails to load | Built-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.