Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

cjholowatyj's avatar

Rendering Vue from within Markdown(It) ?

I've been trying (without much success) to get custom Vue SFC components to render within Markdown content. I am using Vue 3 Composition API (with script setup format). I've resigned myself to the fact that implementing it in Laravel Nova isn't worth the effort, but I'd like to at least be able to have markdown fields where I can mix in Vue components within Markdown content and render it on the front end of my website...

I've tried implementing it with MarkdownIt and MarkdownItContainer, using recommendations from both ChatGPT 3.0 and 3.5, but no matter what I try, it only renders the component tags as text within a paragraph tag.

I see that VitePress supports this, but I cannot seem to find a precise example of this in the source code. Has anyone implemented this successfully who might be able to provide an example implementation for me to reference?

0 likes
3 replies
LaryAI's avatar
Level 58

To render Vue components within Markdown content, you can use a combination of MarkdownIt and Vue's render function. Here's an example implementation:

  1. Install the required dependencies:
npm install markdown-it @vue/runtime-dom
  1. Create a new Vue component that will handle the rendering of Markdown content:
<template>
  <div v-html="renderedMarkdown"></div>
</template>

<script>
import MarkdownIt from 'markdown-it';
import { h, createApp } from 'vue';

export default {
  props: ['markdown'],
  data() {
    return {
      renderedMarkdown: '',
    };
  },
  mounted() {
    const md = new MarkdownIt();

    // Parse the Markdown content
    const parsedMarkdown = md.parse(this.markdown, {});

    // Traverse the parsed Markdown and replace Vue component tags with their rendered output
    const traverse = (tokens) => {
      for (let i = 0; i < tokens.length; i++) {
        const token = tokens[i];

        if (token.type === 'container_vue_component_open') {
          const componentName = token.info.trim();
          const componentProps = token.meta;

          // Create a new Vue app instance and render the component
          const app = createApp({
            render: () => h(componentName, componentProps),
          });

          // Mount the app and get the rendered HTML
          const renderedHtml = app.mount().$el.outerHTML;

          // Replace the component tag with the rendered HTML
          token.type = 'html_inline';
          token.tag = '';
          token.content = renderedHtml;
        }

        if (token.children) {
          traverse(token.children);
        }
      }
    };

    traverse(parsedMarkdown);

    // Render the modified Markdown content
    this.renderedMarkdown = md.renderer.render(parsedMarkdown, {});
  },
};
</script>
  1. Use the new component in your Vue application:
<template>
  <div>
    <markdown-renderer :markdown="markdownContent"></markdown-renderer>
  </div>
</template>

<script>
import MarkdownRenderer from './MarkdownRenderer.vue';

export default {
  components: {
    MarkdownRenderer,
  },
  data() {
    return {
      markdownContent: `
# My Markdown Content

This is some **bold** text.

:::vue_component MyVueComponent
prop1: value1
prop2: value2
:::
      `,
    };
  },
};
</script>

In this example, the MarkdownRenderer component takes a markdown prop, which contains the Markdown content. It uses markdown-it to parse the Markdown and then traverses the parsed tokens to find Vue component tags (e.g., :::vue_component MyVueComponent). It creates a new Vue app instance and renders the component using the render function. The rendered HTML is then inserted back into the Markdown content, and the modified Markdown is rendered using v-html.

Note that this is a simplified example, and you may need to adjust it based on your specific requirements and setup.

martinbean's avatar

@cjholowatyj Why are you trying to render Vue components in the middle of a block of Markdown text? That sounds like a huge security risk 😬

cjholowatyj's avatar

@martinbean I have a Vue component that renders images, and I want to use markdown for blog posts and other static content, but want the functionality I’ve already coded into the image Vue component as well. Is there a better way to do that? (For context, I’m using Laravel Nova as an admin portal and would have clients able to write their own content using markdown)

Please or to participate in this conversation.