Files
llm-in-text/milkdown-docs/recipes/vue.md
ydy0615 d9ab341223 Add documentation for using Milkdown with various frameworks
- Created a new document for using components in Milkdown.
- Added a guide for using plugins in Milkdown, including toggling plugins programmatically and listing official plugins.
- Introduced a recipe for integrating Milkdown with Angular, including installation steps and component creation.
- Added a recipe for using Milkdown with Next.js, detailing installation and component setup.
- Created a guide for integrating Milkdown with NuxtJS, including installation and component creation.
- Added a comprehensive guide for using Milkdown with React, covering both Crepe and core Milkdown usage.
- Introduced a recipe for SolidJS integration with Milkdown, including installation and component creation.
- Added a guide for using Milkdown with Svelte, detailing installation and component setup.
- Created a comprehensive guide for integrating Milkdown with Vue, covering both Crepe and core Milkdown usage.
- Added a recipe for using Milkdown with Vue2, including installation and component creation.
2026-01-17 14:18:08 +08:00

6.1 KiB

Vue Integration

Milkdown provides first-class Vue support with dedicated packages and hooks for seamless integration. You can choose between Crepe, our feature-rich WYSIWYG editor, or the core Milkdown editor for more customization options.

Vue version should be 3.x

Using Crepe


Crepe is a powerful, feature-rich Markdown editor built on top of Milkdown that provides a more user-friendly editing experience.

Installation

npm install @milkdown/crepe @milkdown/vue @milkdown/kit

Implementation

<!-- MilkdownEditor.vue -->
<template>
  <Milkdown />
</template>

<script>
import { Crepe } from "@milkdown/crepe";
import { Milkdown, MilkdownProvider, useEditor } from "@milkdown/vue";
import { defineComponent } from "vue";

export default defineComponent({
  name: "MilkdownEditor",
  components: {
    Milkdown,
  },
  setup: () => {
    const { get } = useEditor((root) => {
      return new Crepe({ root });
    });
  },
});
</script>

<!-- MilkdownEditorWrapper.vue -->
<template>
  <MilkdownProvider>
    <MilkdownEditor />
  </MilkdownProvider>
</template>

<script>
import { MilkdownProvider } from "@milkdown/vue";
import { defineComponent } from "vue";

export default defineComponent({
  name: "MilkdownEditorWrapper",
  components: {
    MilkdownProvider,
  },
});
</script>

Online Demo

::iframe{src="https://stackblitz.com/github/Milkdown/examples/tree/main/vue-crepe"}

Using Milkdown


For more advanced use cases or when you need full control over the editor's configuration, you can use the core Milkdown editor directly.

Install Dependencies

npm install @milkdown/vue @milkdown/kit @milkdown/theme-nord

Basic Usage

Here's a minimal example to get started:

<!-- MilkdownEditor.vue -->
<template>
  <Milkdown />
</template>

<script>
import { Editor, rootCtx } from "@milkdown/kit/core";
import { commonmark } from "@milkdown/kit/preset/commonmark";
import { nord } from "@milkdown/theme-nord";
import { Milkdown, useEditor } from "@milkdown/vue";
import { defineComponent } from "vue";

export default defineComponent({
  name: "MilkdownEditor",
  components: {
    Milkdown,
  },
  setup: () => {
    const { get } = useEditor((root) =>
      Editor.make()
        .config(nord)
        .config((ctx) => {
          ctx.set(rootCtx, root);
        })
        .use(commonmark),
    );
  },
});
</script>

<!-- MilkdownEditorWrapper.vue -->
<template>
  <MilkdownProvider>
    <MilkdownEditor />
  </MilkdownProvider>
</template>

<script>
import { MilkdownProvider } from "@milkdown/vue";
import { defineComponent } from "vue";

export default defineComponent({
  name: "MilkdownEditorWrapper",
  components: {
    MilkdownProvider,
  },
});
</script>

::iframe{src="https://stackblitz.com/github/Milkdown/examples/tree/main/vue-commonmark"}

Advanced Usage


Accessing Editor Instance

The useInstance() hook can only be used within components that are children of MilkdownProvider. It returns a tuple containing a loading state and a getter function to access the editor instance.

<!-- EditorControls.vue -->
<template>
  <button @click="handleSave" :disabled="isLoading">Save</button>
</template>

<script>
import { useInstance } from "@milkdown/vue";
import { defineComponent } from "vue";

export default defineComponent({
  name: "EditorControls",
  setup: () => {
    const [isLoading, getInstance] = useInstance();

    const handleSave = () => {
      if (isLoading.value) return;

      const editor = getInstance();
      if (!editor) return;

      const content = editor.getMarkdown();
      // Do something with the content
    };

    return {
      isLoading,
      handleSave,
    };
  },
});
</script>

<!-- EditorWithControls.vue -->
<template>
  <MilkdownProvider>
    <MilkdownEditor />
    <EditorControls />
  </MilkdownProvider>
</template>

<script>
import { MilkdownProvider } from "@milkdown/vue";
import { defineComponent } from "vue";

export default defineComponent({
  name: "EditorWithControls",
  components: {
    MilkdownProvider,
  },
});
</script>

Best Practices

  1. Component Structure

    • Keep the editor component separate from business logic
    • Wrap the editor with MilkdownProvider at the highest necessary level
    • Use TypeScript for better type safety
  2. Performance

    • Memoize the editor configuration if it's complex
    • Use Vue's shallowRef for editor instance if needed
    • Avoid unnecessary re-renders of the editor

Common Use Cases

Form Integration

<template>
  <form @submit.prevent="handleSubmit">
    <MilkdownEditorWrapper />
    <button type="submit" :disabled="isLoading">Submit</button>
  </form>
</template>

<script>
import { useInstance } from "@milkdown/vue";
import { defineComponent } from "vue";

export default defineComponent({
  name: "FormWithEditor",
  setup: () => {
    const [isLoading, getInstance] = useInstance();

    const handleSubmit = () => {
      if (isLoading.value) return;

      const editor = getInstance();
      if (!editor) return;

      const content = editor.getMarkdown();
      // Submit form with content
    };

    return {
      isLoading,
      handleSubmit,
    };
  },
});
</script>

Auto-save

<template>
  <Milkdown />
</template>

<script>
import { Editor, rootCtx } from "@milkdown/kit/core";
import { commonmark } from "@milkdown/kit/preset/commonmark";
import { listener, listenerCtx } from "@milkdown/kit/plugin/listener";
import { Milkdown, useEditor } from "@milkdown/vue";
import { defineComponent } from "vue";

export default defineComponent({
  name: "AutoSaveEditor",
  components: {
    Milkdown,
  },
  setup: () => {
    const { get } = useEditor((root) =>
      Editor.make()
        .config((ctx) => {
          ctx.set(rootCtx, root);
          // Add markdown listener for auto-save
          ctx.get(listenerCtx).markdownUpdated((ctx, markdown) => {
            // Save content to your backend or storage
            saveToBackend(markdown);
          });
        })
        .use(commonmark)
        .use(listener),
    );
  },
});
</script>

More Examples