vue-mdr

Tranform markdown string to vue component with `mdast-> hast-> vue node`
3,378 57
Install
npm install vue-mdr
Latest Version:0.1.3
Vue:3
License:ISC
Last Updated:Apr 30, 2026
Links: GitHub  ·  npm
Maintainer: linzhe_tools

vue-mdr logo

VueMarkdownRenderer

A config-driven, high-performance Markdown renderer for Vue, designed for LLM streaming, rich code blocks, and embedded visual components.

live demo


Core Idea

Unlike traditional Markdown components that rely on many reactive props, VueMarkdownRenderer uses a single static configuration to define rendering behavior.

const MarkdownRenderer = createMarkdownRenderer(config);
  • Rendering capabilities are defined once
  • The returned renderer component is pure and predictable
  • Avoids the mental overhead of “is this prop reactive?”

This design is intentional: 99% of Markdown rendering scenarios do not require runtime mutation of render rules.


Installation

npm install vue-mdr

Basic Usage

import { createMarkdownRenderer } from "vue-mdr";
export const MarkdownRenderer = createMarkdownRenderer({
  // configuration here
});
<template>
  <MarkdownRenderer :source="markdownText" theme="light" />
</template>

Full Configuration Example

Below is a complete example matching the latest API design.

import { createMarkdownRenderer } from "vue-mdr";

import "katex/dist/katex.min.css";
import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex";
import type { Plugin } from "unified";

import {
  BarChart,
  CodeBlockRenderer,
  MermaidRenderer,
  EchartRenderer,
  Placeholder,
} from "./components";

export const MarkdownRenderer = createMarkdownRenderer({
  /**
   * Custom Vue components rendered via `component-json` blocks
   */
  componentsMap: {
    BarChart,
    Placeholder,
    // The rendering of HTML tags can also be overridden using this componentMap
    // !However, besides `pre`, which already uses it built-in
    a: ARenderer,
    img: ImageRenderer,
  },

  /**
   * Custom code block renderer
   */
  codeBlock: {
    renderer: CodeBlockRenderer,
  },

  /**
   * ECharts support
   */
  echart: {
    renderer: EchartRenderer,
    placeholder: Placeholder,
  },

  /**
   * Mermaid diagrams
   */
  mermaid: {
    renderer: MermaidRenderer,
  },

  /**
   * Markdown / HTML plugins
   */
  remarkPlugins: [remarkMath],
  rehypePlugins: [rehypeKatex as unknown as Plugin],
  /**
   * built-in remarkRehype plugin options
   */
  remarkRehypeOptions: {
    allowDangerousHtml: true,
  },
  /**
   * built-in rehypeSanitize plugin custom schema
   */
  rehypeSanitizeSchema: {
    attributes: {
      "*": ["className", "style"],
    },
  },
});

Design Philosophy

1. Static Configuration over Reactive Props

createMarkdownRenderer({
  mermaid: { renderer },
  echart: { renderer },
  codeBlock: { renderer },
});
  • No runtime mutation
  • No watchers
  • No ambiguous reactivity expectations

The renderer is configured, not controlled.


2. Capability-based Rendering

Each feature is opt-in:

Feature Config Key
Code blocks codeBlock
Mermaid mermaid
ECharts echart
Vue components componentsMap
LaTeX remarkPlugins / rehypePlugins

If you don’t configure it, it doesn’t exist.


Custom Code Block Rendering

Your CodeBlockRenderer receives:

interface Props {
  highlightVnode: VNode;
  language: string;
}

This allows you to implement:

  • Copy buttons
  • Language labels
  • Custom headers
  • Animations
  • Streaming-friendly UI
codeBlock: {
  renderer: CodeBlockRenderer,
}

Rendering Vue Components (component-json)

```component-json {"placeholder": "Placeholder"}
{
  "type": "BarChart",
  "props": {
    "chartData": {
      "categories": ["A", "B", "C"],
      "seriesData": [10, 20, 30]
    }
  }
}
```
{"type":"BarChart","props":{"chartData":{"categories":["type1","type2","type3","type4","type5","type6","type7","type8","type9","type10","type11","type12","type13","type14","type15","type16","type17","type18","type19","type20"],"seriesData":[100,200,150,180,120,130,170,160,190,210,220,140,125,155,165,175,185,195,205,215]}}}

Rendering ECharts

```echarts
{
  "title": {
    "text": "数据对比趋势变化",
    "left": "center"
  },
  "tooltip": {
    "trigger": "axis",
    "axisPointer": {
      "type": "cross",
      "crossStyle": { "color": "#999" }
    },
    "formatter": "{b}<br/>{a0}: {c0}"
  },
  "legend": {
    "data": ["本期"],
    "top": "bottom"
  },
  "grid": {
    "left": "3%",
    "right": "4%",
    "bottom": "10%",
    "containLabel": true
  },
  "xAxis": [
    {
      "type": "category",
      "data": ["xxx", "zzz"],
      "axisPointer": { "type": "shadow" }
    }
  ],
  "yAxis": [
    {
      "type": "value",
      "name": "数值",
      "min": 0,
      "axisLabel": { "formatter": "{value}" }
    }
  ],
  "series": [
    {
      "name": "本期",
      "type": "bar",
      "data": [5061.1429, 504.8844],
      "itemStyle": { "color": "#3ba272" }
    }
  ]
}
```

Configuration:

echart: {
  renderer: EchartRenderer,
  placeholder: Placeholder,
}
{
  "title": {
    "text": "数据对比趋势变化",
    "left": "center"
  },
  "tooltip": {
    "trigger": "axis",
    "axisPointer": {
      "type": "cross",
      "crossStyle": { "color": "#999" }
    },
    "formatter": "{b}<br/>{a0}: {c0}"
  },
  "legend": {
    "data": ["本期"],
    "top": "bottom"
  },
  "grid": {
    "left": "3%",
    "right": "4%",
    "bottom": "10%",
    "containLabel": true
  },
  "xAxis": [
    {
      "type": "category",
      "data": ["xxx", "zzz"],
      "axisPointer": { "type": "shadow" }
    }
  ],
  "yAxis": [
    {
      "type": "value",
      "name": "数值",
      "min": 0,
      "axisLabel": { "formatter": "{value}" }
    }
  ],
  "series": [
    {
      "name": "本期",
      "type": "bar",
      "data": [5061.1429, 504.8844],
      "itemStyle": { "color": "#3ba272" }
    }
  ]
}

Mermaid Diagrams

```mermaid
sequenceDiagram
  Alice->>Bob: Hello
  Bob-->>Alice: Hi!
```
mermaid: {
  renderer: MermaidRenderer,
}
sequenceDiagram
  Alice->>Bob: Hello
  Bob-->>Alice: Hi!

LaTeX Support

remarkPlugins: [remarkMath],
rehypePlugins: [rehypeKatex],

$$ \begin{align} \nabla \times \vec{\mathbf{B}} -, \frac1c, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \ \nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \ \nabla \times \vec{\mathbf{E}}, +, \frac1c, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \ \nabla \cdot \vec{\mathbf{B}} & = 0 \end{align} $$

Related Packages

vue-markdown-stream

🌐 English | 简体中文

7,709 16
vue-stream-markdown

Streaming markdown output, Useful for text streams like LLM outputs.

447,910 243
markdown-it-mdc

Markdown Components (MDC) support for markdown-it

1,264,498 152
@cocoar/vue-markdown-core

Core markdown parsing utilities for @cocoar/vue-markdown

15,079 0
kokk

A beautiful way to create a documentation from markdown, and insert a vue compon...

10,142 49

Version History

Version Released Vue License
0.1.3 ^3.2.0 ISC
0.1.2 ^3.2.0 ISC
0.1.1 ^3.2.0 ISC
0.1.0 ^3.2.0 ISC
0.0.16 ^3.2.0 ISC
Pre-releases (5)
Version Released Vue License
0.0.3-alpha-5 ^3.5.13 ISC
0.0.3-alpha-4 ^3.5.13 ISC
0.0.3-alpha-3 ^3.5.13 ISC
0.0.3-alpha-2 ^3.5.13 ISC
0.0.3-alpha-1 ^3.5.13 ISC