2.10.0
中文
Skip to content

Overview ​

components can replace standard HTML tags or custom tags with Vue components. In conversation scenarios, tags are often handed to Think, Sources, or ThoughtChain; code blocks are handed to HighlightCode / Mermaid; you can also map your own chart tags.

Related pages:

Basic usage ​

ts
import { defineComponent, h } from 'vue';
import { XMarkdown } from '@ant-design-x-vue/x-markdown';

const CustomHeading = defineComponent({
  setup(_, { slots }) {
    return () => h('h1', { style: { color: '#1890ff' } }, slots.default?.());
  },
});

// <XMarkdown content="# Hello World" :components="{ h1: CustomHeading }" />

Demo:

Custom Blue Title

Normal H2

Paragraph with bold and italic.

Map HTML tags to custom Vue components through components, which can receive props like domNode, streamStatus, children, etc.

Performance tips ​

1. Avoid defining components inline in render functions ​

ts
// ❌ 每次渲染都会创建新组件引用
h(XMarkdown, {
  components: {
    h1: (props) => h('h1', props, props.children),
  },
});

// ✅ 在模块或 setup 外预先定义
const Heading = (props) => h('h1', props, props.children);
h(XMarkdown, { components: { h1: Heading } });

2. Keep components references stable ​

Create the components object once inside setup, or use computed / constants to avoid creating a new object on every parent component update, which causes unnecessary re-rendering of child subtrees.

3. Pass business data through componentsProps ​

Custom components often require business data (theme, callbacks, etc.). If you wrap it in an inline function and pass it as props, a new component reference is created on every render, causing repeated unmounting and rebuilding in streaming scenarios. Use componentsProps to pass additional props by tag name, and keep the components reference stable:

ts
const components = { 'custom-chart': CustomChart };
const componentsProps = computed(() => ({
  'custom-chart': { theme: theme.value, onSelect },
}));

h(XMarkdown, { components, componentsProps: componentsProps.value, content });

Merging rules (aligned with React @ant-design/x-markdown@2.10):

  • Attributes with the same name are determined by componentsProps (for example, overriding title on HTML)
  • className / class will be concatenated, with class names from componentsProps coming first
  • Internally computed domNode, streamStatus, children (and lang, block for code) cannot be overridden

Streaming render and streamStatus ​

XMarkdown will pass streamStatus to custom components, indicating whether the tag has been closed, which is useful for implementing loading UI.

ts
const StreamingComponent = (props: { streamStatus?: 'loading' | 'done'; children?: unknown }) => {
  if (props.streamStatus === 'loading') {
    return h('div', { class: 'loading' }, 'Loading...');
  }
  return h('div', null, props.children as any);
};

Status checks ​

  • loading: the content of this block is still being streamed (common for custom HTML tags or fenced code)
  • 'done': This block is closed. Indent code blocks have no clear terminator and are always 'done'
  • 'code' can also distinguish between block-level and inline by 'data-state' / 'block'

Data fetching example ​

Components can read data directly from the Markdown property or send a request after 'streamStatus === 'done'':

ts
import { ref, watchEffect } from 'vue';

const UserCard = {
  props: ['domNode', 'streamStatus'],
  setup(props) {
    const user = ref(null);
    watchEffect(async () => {
      const username = props.domNode?.attribs?.['data-username'];
      if (username && props.streamStatus === 'done') {
        user.value = await fetch(`/api/users/${username}`).then((r) => r.json());
      }
    });
    return () =>
      user.value
        ? h('div', { class: 'user-card' }, user.value.name)
        : h('div', null, 'Loading...');
  },
};

Supported tag mapping ​

Standard HTML tags ​

TagComponent Name
aa
h1–h6h1–h6
pp
imgimg
tabletable
ul / ol / liul / ol / li
code / precode / pre

Custom tags ​

ts
// 任意自定义标签都可以映射
// <XMarkdown :components="{ 'my-component': MyComponent, 'user-card': UserCard }" />

ComponentProps ​

PropDescriptionTypeDefault
domNodeParsed DOM Node InformationDOMNode-
streamStatus'loading' / 'done', see above'loading' | 'done'-
childrenSubcontentVNode[] | string-
The restStandard HTML properties vs. custom data propertiesRecord\<string, any\>-

For more general tables, see Code Example · API。