Files
llm-in-text/milkdown-docs/plugin/example-iframe-plugin.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

3.7 KiB

Example: Iframe Plugin

This guide demonstrates how to create a custom iframe syntax plugin for Milkdown. This plugin allows you to embed iframes directly in your markdown content using a simple directive syntax.

Overview


The iframe plugin enables you to embed external web content using the following syntax:

::iframe{src="https://example.com"}

This will render as an embedded iframe in your document.

Implementation Steps


To create a custom syntax plugin in Milkdown, we need to implement five key components:

  1. Remark Plugin: Parse the custom syntax
  2. Schema Definition: Define the node structure
  3. Parser: Convert markdown to ProseMirror nodes
  4. Serializer: Convert ProseMirror nodes back to markdown
  5. Input Rules: Handle user input

Let's implement each component:

1. Remark Plugin


First, we use the remark-directive plugin to support our custom syntax. This plugin allows us to define custom directives in markdown.

import directive from "remark-directive";
import { $remark } from "@milkdown/kit/utils";

const remarkDirective = $remark("remarkDirective", () => directive);

2. Schema Definition


Next, we define the schema for our iframe node. The schema specifies how the node behaves and appears in the editor.

import { $node } from "@milkdown/kit/utils";
import { Node } from "@milkdown/kit/prose/model";

const iframeNode = $node("iframe", () => ({
  group: "block", // Block-level node
  atom: true, // Cannot be split
  isolating: true, // Cannot be merged with adjacent nodes
  marks: "", // No marks allowed
  attrs: {
    src: { default: null }, // URL attribute
  },
  parseDOM: [
    {
      tag: "iframe",
      getAttrs: (dom) => ({
        src: (dom as HTMLElement).getAttribute("src"),
      }),
    },
  ],
  toDOM: (node: Node) => [
    "iframe",
    { ...node.attrs, contenteditable: false }, // Prevent editing iframe content
    0,
  ],
}));

3. Parser


The parser converts our markdown syntax into ProseMirror nodes. It looks for the leafDirective type with the name "iframe".

parseMarkdown: {
  match: (node) => node.type === 'leafDirective' && node.name === 'iframe',
  runner: (state, node, type) => {
    state.addNode(type, { src: (node.attributes as { src: string }).src });
  },
},

4. Serializer


The serializer converts ProseMirror nodes back to markdown format.

toMarkdown: {
  match: (node) => node.type.name === 'iframe',
  runner: (state, node) => {
    state.addNode('leafDirective', undefined, undefined, {
      name: 'iframe',
      attributes: { src: node.attrs.src },
    });
  },
},

5. Input Rules


Input rules handle user typing and convert the syntax into an iframe node.

import { InputRule } from "@milkdown/kit/prose";
import { $inputRule } from "@milkdown/kit/utils";

const iframeInputRule = $inputRule(
  () =>
    new InputRule(
      /::iframe\{src\="(?<src>[^"]+)?"?\}/,
      (state, match, start, end) => {
        const [okay, src = ""] = match;
        const { tr } = state;
        if (okay) {
          tr.replaceWith(start - 1, end, iframeNode.type().create({ src }));
        }
        return tr;
      },
    ),
);

Usage


To use the iframe plugin, add it to your Milkdown editor configuration:

import { Editor } from "@milkdown/kit/core";
import { commonmark } from "@milkdown/kit/preset/commonmark";

Editor.make()
  .use([remarkDirective, iframeNode, iframeInputRule])
  .use(commonmark)
  .create();

Example


Here's a complete example of the iframe plugin in action:

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