English | 中文
Vue 3 port of react-markdown (v10.1.0) - render Markdown as Vue components using the unified / remark / rehype ecosystem.
AI-Generated Project: This project was entirely generated by AI (Kiro AI IDE with Claude). The code, tests, and documentation were all produced through AI-assisted development. While thoroughly tested (58 tests, including property-based tests), please review the code before using in production.
This library is a faithful 1:1 translation of react-markdown to Vue 3. It converts Markdown text to Vue VNodes using the same unified pipeline, so you get the same features, the same plugin ecosystem, and the same behavior - just for Vue.
| react-markdown | vue-markdown | Notes |
|---|---|---|
react/jsx-runtime |
vue/jsx-runtime |
Vue 3.3+ built-in |
Markdown (function component) |
VueMarkdown (defineComponent) |
Synchronous rendering |
MarkdownAsync (async component) |
VueMarkdownAsync (defineAsyncComponent) |
Use with <Suspense> |
MarkdownHooks (hooks component) |
VueMarkdownHooks (defineComponent) |
ref + watchEffect |
unreachable() from devlop |
throw new Error() |
No extra dependency |
| - | elementAttributeNameCase: 'html' |
Required for Vue JSX runtime |
npm install git+https://github.com/lingpotool/vue-markdown.gitOr with pnpm:
pnpm add git+https://github.com/lingpotool/vue-markdown.gitPeer dependency: Vue >= 3.3.0
<script setup>
import VueMarkdown from 'vue-markdown'
</script>
<template>
<VueMarkdown children="# Hello, *world*!" />
</template><script setup>
import VueMarkdown from 'vue-markdown'
import remarkGfm from 'remark-gfm'
const plugins = [remarkGfm]
</script>
<template>
<VueMarkdown
children="| Feature | Status |\n|---|---|\n| Tables | OK |"
:remarkPlugins="plugins"
/>
</template><script setup>
import { VueMarkdownAsync } from 'vue-markdown'
import { markRaw } from 'vue'
const AsyncMd = markRaw(VueMarkdownAsync({
children: '# Async content',
remarkPlugins: [/* async plugins */]
}))
</script>
<template>
<Suspense>
<component :is="AsyncMd" />
<template #fallback>Loading...</template>
</Suspense>
</template><script setup>
import { VueMarkdownHooks } from 'vue-markdown'
import { ref } from 'vue'
const md = ref('# Hello')
</script>
<template>
<VueMarkdownHooks :children="md">
<template #fallback>Loading...</template>
</VueMarkdownHooks>
</template>Synchronous component. Props:
| Prop | Type | Default | Description |
|---|---|---|---|
children |
string |
'' |
Markdown content |
remarkPlugins |
Array |
null |
remark plugins |
rehypePlugins |
Array |
null |
rehype plugins |
remarkRehypeOptions |
Object |
null |
Options for remark-rehype |
components |
Object |
null |
Map tag names to Vue components |
allowedElements |
string[] |
null |
Only allow these elements |
disallowedElements |
string[] |
null |
Disallow these elements |
allowElement |
Function |
null |
Filter function for elements |
unwrapDisallowed |
boolean |
false |
Unwrap disallowed elements (keep children) |
skipHtml |
boolean |
false |
Skip HTML in Markdown |
urlTransform |
Function | null |
defaultUrlTransform |
Transform URLs; pass null to skip |
Factory function that returns an async component. Same options as VueMarkdown. Use with <Suspense>.
Reactive async component. Same props as VueMarkdown plus:
| Prop | Type | Default | Description |
|---|---|---|---|
fallback |
VNode |
null |
Content to show while loading |
Also supports a #fallback slot.
Default URL sanitizer. Allows http, https, irc, ircs, mailto, xmpp protocols and relative URLs. Returns empty string for unsafe protocols.
<script setup>
import VueMarkdown from 'vue-markdown'
import { h } from 'vue'
const components = {
h1: (props) => h('h1', { class: 'title', ...props }, props.children),
a: (props) => h('a', { target: '_blank', ...props }, props.children)
}
</script>
<template>
<VueMarkdown children="# Click [here](https://example.com)" :components="components" />
</template>Custom components receive a node prop containing the HAST element node.
This is a line-by-line translation of react-markdown v10.1.0. All pure logic functions (createProcessor, createFile, defaultUrlTransform, post) are identical. The only differences are Vue-specific adaptations:
elementAttributeNameCase: 'html'intoJsxRuntimecall (required by Vue's JSX runtime)- Vue component model (
defineComponent,defineAsyncComponent) instead of React function components - Vue Composition API (
ref,computed,watchEffect) instead of React hooks urlTransform: nullskips URL processing entirely (react-markdown falls back to default)
npm test58 tests total:
- 26 property-based tests (fast-check) covering 15 correctness properties
- 32 unit tests covering edge cases and integration
- react-markdown - the original project this is ported from
- unified ecosystem - the Markdown/HTML processing pipeline
- Kiro AI IDE - AI-assisted development environment used to generate this project