Skip to content

XMarkdown (External)

使用import { MarkdownRenderer } from 'x-markdown-vue' import 'x-markdown-vue/style'
文档
Edit this pageChangelogFeedback

Introduction

x-markdown-vue is a standalone Markdown rendering library extracted from vue-element-plus-x, purpose-built for rich text display and streaming output in AI chat scenarios.

✨ Features

  • 🚀 Streaming Rendering — Real-time output animation for AI chat, with per-token fade-in effects
  • 📝 GitHub Flavored Markdown — Full GFM support (tables, task lists, strikethrough, etc.)
  • 🎨 Syntax Highlighting — Powered by Shiki with 100+ languages and multiple themes
  • 🧮 LaTeX Math — Inline $...$ and block $$...$$ math formulas
  • 📊 Mermaid Diagrams — Flowcharts, sequence diagrams, Gantt charts, class diagrams, and more
  • 🌗 Dark Mode — Built-in light/dark theme support
  • 🔌 Highly Customizable — Custom render slots, custom attributes, custom code block renderers
  • 🎭 Plugin System — remark and rehype plugin extensions
  • 🔒 Secure — Optional HTML sanitization to prevent XSS attacks

Starting from v2.0.0, vue-element-plus-x no longer bundles Typewriter / XMarkdown / XMarkdownAsync. Install x-markdown-vue separately.

Installation

bash
# pnpm (recommended)
pnpm add x-markdown-vue

# npm
npm install x-markdown-vue

# yarn
yarn add x-markdown-vue

Optional Dependencies

x-markdown-vue uses optional peer dependencies for extra features:

bash
# Syntax highlighting (Shiki)
pnpm add shiki shiki-stream

# Mermaid diagrams
pnpm add mermaid

# LaTeX math formulas (also requires importing the CSS)
pnpm add katex

💡 Tip

If shiki / shiki-stream are not installed, code blocks will fall back to plain text rendering.

Code Examples

Basic Usage

Loading...

Use MarkdownRenderer to render static Markdown content with GFM, task lists, tables, and syntax highlighting.

Streaming Animation

Loading...

Enable enable-animate to add a fade-in animation for each newly appended token — ideal for AI streaming output.

Dark Mode

Light
Loading...

Use the is-dark prop to toggle between light and dark themes. Shiki syntax highlighting themes switch automatically.

Code Block Configuration

Max Height:
Loading...

Configure code block header visibility, max height, and sticky header behavior.

Sticky Code Header with BubbleList

Place MarkdownRenderer inside BubbleList's #content slot so BubbleList becomes the scroll container. When the list is scrolled, the code block header sticks to the top of the list's visible area — never occluded by the page navbar.

💡 Scroll the list down — the code block header will stick to the top of the BubbleList viewport, not the page top.

Place MarkdownRenderer inside BubbleList's #content slot so BubbleList acts as the scroll container. When the list is scrolled, the code block language label and action bar stick to the top of BubbleList's visible area — not the page viewport — so the document navbar never occludes it.

LaTeX Math Formulas

Install katex and import its CSS:

ts
import 'katex/dist/katex.min.css';
Loading...

Enable LaTeX math with enable-latex. Install katex and import its CSS file.

Mermaid Diagrams

Install mermaid first: pnpm add mermaid

Loading...

Enable enable-mermaid to render flowcharts, sequence diagrams, and more. Install mermaid first.

Custom Code Block Actions

Pass a code-block-actions array to add custom buttons to code block headers. Use the show callback to conditionally display buttons per language.

onClick receives a CodeBlockSlotProps object:

PropertyTypeDescription
languagestringCode language
codestringCode content
copy(text: string) => voidCopy function
copiedbooleanWhether copied
collapsedbooleanWhether collapsed
toggleCollapse() => voidToggle collapse
Loading...

Pass a code-block-actions array to add custom buttons to code block headers. Use show to conditionally display buttons.

Custom Mermaid Actions

Pass a mermaid-actions array to add custom buttons to the Mermaid diagram toolbar.

onClick receives a MermaidSlotProps object:

PropertyTypeDescription
showSourceCodebooleanWhether showing source code view
svgstringRendered SVG string
rawContentstringRaw Mermaid source code
isLoadingbooleanWhether currently rendering
zoomIn() => voidZoom in
zoomOut() => voidZoom out
reset() => voidReset zoom
fullscreen() => voidFullscreen
toggleCode() => voidToggle source/diagram view
copyCode() => Promise<void>Copy source code
download() => voidDownload SVG
Loading...

Pass a mermaid-actions array to add custom buttons to the Mermaid diagram toolbar.

Custom Code Block Renderer

Loading...

Use code-x-render to replace the default code block renderer for specific languages with a fully custom component.

Custom Attributes

Loading...

Add custom HTML attributes to any Markdown element via custom-attrs. Useful for styling or behavior customization.

Custom Slots

Supported slot names:

SlotDescription
heading / h1 ~ h6Headings
code / inline-code / block-codeCode
blockquoteBlockquote
list / ul / ol / li / list-itemLists
table / thead / tbody / tr / td / thTables
aLinks
imgImages
p / strong / emInline elements
Any standard HTML tag name
Loading...

Override Markdown element rendering using named slots for complete control over the HTML output.

Custom Table with el-table

Use the #table slot to replace all GFM tables in Markdown with el-table. The hast node passed to the slot contains the full table structure — a helper extracts columns and rows, then feeds them to el-table.

Loading...

Override Markdown GFM table rendering via the #table slot. The slot exposes children (a function returning a VNode array) — recursively walk <thead> / <tbody> to collect columns and rows, then render with el-table for borders, stripes, and more.

Custom Code Block Components (el-table & my-echarts)

Define your own "language" tags via code-x-render. Write JSON inside a fenced code block, set the language to el-table or my-echarts, and the renderer maps each block to a real Vue component.

  • el-table — parses JSON { columns, rows } and renders as an Element Plus table
  • my-echarts — parses an ECharts option JSON and renders as an interactive chart
Loading...

Use code-x-render to render specific code block languages as Vue components:

  • el-table blocks — render JSON data as an Element Plus table
  • my-echarts blocks — render ECharts JSON config as an interactive chart

Plugin System

Extend the parsing pipeline with remark/rehype plugins:

ts
import remarkEmoji from 'remark-emoji';
import rehypeSlug from 'rehype-slug';

const remarkPlugins = [remarkEmoji];
const rehypePlugins = [rehypeSlug];
vue
<MarkdownRenderer
  :markdown="content"
  :remark-plugins="remarkPlugins"
  :rehype-plugins="rehypePlugins"
/>

Security

Enable sanitize to strip unsafe HTML before rendering, preventing XSS:

vue
<MarkdownRenderer
  :markdown="content"
  :sanitize="true"
  :sanitize-options="{ allowedTags: ['b', 'i', 'em', 'strong', 'a'] }"
/>

Streaming Custom Code Blocks (with Skeleton Placeholder)

A real-world AI streaming scenario: while the JSON is being assembled, show an el-skeleton placeholder; once the data becomes parseable, swap to the real el-table / el-form / my-echarts component.

Loading...

A real-world AI streaming scenario: while the JSON inside an `el-table` / `el-form` / `my-echarts` code block is still being assembled, show an el-skeleton placeholder; once the JSON becomes valid, swap to the actual component.

The trick: inside code-x-render, try JSON.parse(props.raw.content). Parse fails → skeleton; parse succeeds → real component. Combined with streaming updates this naturally produces a "skeleton → content" transition.

API

Props

PropTypeDefaultDescription
markdownstring''Markdown text content
allow-htmlbooleanfalseAllow raw HTML in content
enable-latexbooleantrueEnable LaTeX math rendering
enable-animatebooleanfalseEnable streaming animation
enable-breaksbooleantrueConvert single newlines to <br>
enable-gfmbooleantrueEnable GitHub Flavored Markdown
enable-shikibooleantrueEnable Shiki syntax highlighting
enable-mermaidbooleantrueEnable Mermaid diagram rendering
is-darkbooleanfalseDark mode
shiki-theme[BuiltinTheme, BuiltinTheme]['vitesse-light', 'vitesse-dark']Shiki light/dark themes
show-code-block-headerbooleantrueShow code block header bar
sticky-code-block-headerbooleanfalseSticky code block header
code-max-heightstringundefinedMax height of code blocks, e.g. '300px'
code-block-actionsCodeBlockAction[][]Custom code block action buttons
mermaid-actionsMermaidAction[][]Custom Mermaid action buttons
mermaid-configobject{}Mermaid initialization config
code-x-renderobject{}Custom code block render functions
custom-attrsCustomAttrs{}Custom element attributes
remark-pluginsPluggableList[]remark plugins (run after built-ins)
remark-plugins-aheadPluggableList[]remark plugins (run before built-ins)
rehype-pluginsPluggableList[]rehype plugins (run after built-ins)
rehype-plugins-aheadPluggableList[]rehype plugins (run before built-ins)
rehype-optionsobject{}rehype processor options
sanitizebooleanfalseEnable HTML sanitization (XSS prevention)
sanitize-optionsSanitizeOptions{}Sanitization options

CodeBlockAction Type

ts
interface CodeBlockAction {
  key: string;
  icon?: Component | FunctionalComponent | string;
  title?: string;
  onClick?: (props: CodeBlockSlotProps) => void;
  disabled?: boolean;
  class?: string;
  style?: Record<string, string>;
  show?: (props: CodeBlockSlotProps) => boolean;
}

MermaidAction Type

ts
interface MermaidAction {
  key: string;
  icon?: Component | FunctionalComponent | string;
  title?: string;
  onClick?: (props: MermaidSlotProps) => void;
  disabled?: boolean;
  class?: string;
  style?: Record<string, string>;
  show?: (props: MermaidSlotProps) => boolean;
}