😁 Basic Usage
It has been developing iteratively, so update the latest version please. Publish logs: releases
🤓 CDN
Use production version in html directly:
            
            
              html
              
              
              
            
          
          <!doctype html>
<html lang="en">
  <head>
    <link href="https://unpkg.com/md-editor-rt@6.1.0/lib/style.css" rel="stylesheet" />
    <script type="importmap">
      {
        "imports": {
          "react": "https://esm.sh/react",
          "react-dom": "https://esm.sh/react-dom",
          "react/jsx-runtime": "https://esm.sh/react/jsx-runtime",
          "react-dom/client": "https://esm.sh/react-dom/client",
          "medium-zoom": "https://esm.sh/medium-zoom",
          "lru-cache": "https://esm.sh/lru-cache",
          "codemirror": "https://esm.sh/codemirror",
          "lucide-react": "https://cdn.jsdelivr.net/npm/lucide-react/dist/esm/lucide-react.js",
          "@vavt/": "https://esm.sh/@vavt/",
          "@codemirror/": "https://esm.sh/@codemirror/",
          "@lezer/": "https://esm.sh/@lezer/",
          "markdown-it": "https://esm.sh/markdown-it",
          "markdown-it-image-figures": "https://esm.sh/markdown-it-image-figures",
          "markdown-it-sub": "https://esm.sh/markdown-it-sub",
          "markdown-it-sup": "https://esm.sh/markdown-it-sup",
          "md-editor-rt": "https://esm.sh/md-editor-rt@6.1.0"
        }
      }
    </script>
  </head>
  <body>
    <div id="root"></div>
    <script type="module">
      import React from 'react';
      import ReactDOMClient from 'react-dom/client';
      import { jsx as _jsx } from 'react/jsx-runtime';
      import { MdEditor } from 'md-editor-rt';
      ReactDOMClient.createRoot(document.getElementById('root')).render(
        _jsx(MdEditor, { value: 'Hello Editor!!', floatingToolbars: ['bold'] })
      );
    </script>
  </body>
</html>🤖 Npm Install
          
            
        - 
          
          
        
- 
          
          
        
shell
        
        shell
        
        npm install md-editor-rtyarn add md-editor-rtWhen using server-side rendering, make sure to set  Starting from version 5.0, there is no such limitation.editorId to a constant value.
🤓 Jsx Template
            
            
              jsx
              
              
              
            
          
          import { useState } from 'react';
import { MdEditor } from 'md-editor-rt';
import 'md-editor-rt/lib/style.css';
export default () => {
  const [text, setText] = useState('hello md-editor-rt!');
  return <MdEditor modelValue={text} onChange={setText} />;
};📖 Preview Only
            
            
              jsx
              
              
              
            
          
          import { useState } from 'react';
import { MdPreview, MdCatalog } from 'md-editor-rt';
import 'md-editor-rt/lib/preview.css';
export default () => {
  const [id] = useState('preview-only');
  const [scrollElement] = useState(document.documentElement);
  const [text] = useState('hello md-editor-rt!');
  return (
    <>
      <MdPreview id={id} modelValue={text} />
      <MdCatalog editorId={id} scrollElement={scrollElement} />
    </>
  );
};When using server-side rendering, scrollElement should be of string type, eg: html, body, #id, .class.
🎛 Used in Web Component
Complete example reference the sample project provided in the source code.
Here are the precautions:
- The image zoom-in view feature is ineffective; implementation needs to be done manually!!!
- Do not use CDN to reference dependency libraries by default; refer to [Import All Library]!!!
🥂 Api usage
Usages of some APIs.
🥶 Customize Shortcut Key
- 
Source code for built-in shortcut key configuration: commands.ts. They have been added as extensions to codemirror.
- 
The basic principle of replacing or deleting shortcut keys is to find the corresponding extension, and handle it. 
- 
The first input parameter extensionsofcodeMirrorExtensionsis an array, useextensions[i].typeto distinguish the type.
💅 Modify Shortcut Key
Change Ctrl-b to Ctrl-m
            
            
              js
              
              
              
            
          
          import { config } from 'md-editor-rt';
import { keymap } from '@codemirror/view';
config({
  codeMirrorExtensions(extensions, { keyBindings }) {
    // 1. Remove the default shortcut key extension first
    const newExtensions = [...extensions].filter((item) => {
      return item.type !== 'keymap';
    });
    // 2. Reference the source code for shortcut key configuration
    // Find the location of the configuration item for CtrlB in keyBindings
    const CtrlB = keyBindings.find((i) => i.key === 'Ctrl-b');
    // 3. Document for configuring shortcut keys of codemirror
    // https://codemirror.net/docs/ref/#commands
    const CtrlM = {
      // We need the run method in CtrlB here
      ...CtrlB,
      key: 'Ctrl-m',
      mac: 'Cmd-m'
    };
    // 4. Add the modified shortcut key to the array
    const newKeyBindings = [CtrlM, ...keyBindings.filter((i) => i.key !== 'Ctrl-b')];
    newExtensions.push({
      type: 'newKeymap',
      extension: keymap.of(newKeyBindings)
    });
    return newExtensions;
  }
});✂️ Delete Shortcut Key
Disable all shortcut keys
            
            
              js
              
              
              
            
          
          import { config } from 'md-editor-rt';
config({
  codeMirrorExtensions(extensions) {
    // 1. Remove default shortcut key extensions
    const newExtensions = [...extensions].filter((item) => {
      return item.type !== 'keymap';
    });
    // 2. Return extension list
    return newExtensions;
  }
});💉 Add Shortcut Key
If you want to insert content into the edit box, you need to use the insert method bound on the instance of editor, reference: Insert content into the edit box.
If you are not using config in the component where the editor is located, you are unable to obtain instance of editor at this time. You may need to use EventBus.
Add shortcut key Ctrl+m, to insert a marking module into the editing box(==mark==)
index.ts
            
            
              js
              
              
              
            
          
          import { config } from 'md-editor-rt';
import { keymap, KeyBinding } from '@codemirror/view';
// If you used EventBus
import bus from '@/utils/event-bus';
config({
  codeMirrorExtensions(extensions, { keyBindings }) {
    // 1. Remove the default shortcut key extension first
  const newExtensions = [...extensions].filter((item) => {
      return item.type !== 'keymap';
    });
    // 2. Create a new shortcut key configuration, reference: https://codemirror.net/docs/ref/#commands
    const CtrlM: KeyBinding = {
      key: 'Ctrl-m',
      mac: 'Cmd-m',
      run: () => {
        bus.emit('insertMarkBlock');
        return true;
      },
    };
    // 4. Add a new shortcut key to the array
    const newKeyBindings = [...keyBindings, CtrlM];
    newExtensions.push({
      type: 'newKeymap',
      extension: keymap.of(newKeyBindings),
    });
    return newExtensions;
  },
});Next, listening 'insertMarkBlock' in the component where the editor is located
App.tsx
            
            
              tsx
              
              
              
            
          
          import { useState, useRef, useEffect } from 'react';
import { MdEditor, ExposeParam } from 'md-editor-rt';
// If you used EventBus
import bus from '@/utils/event-bus';
const App = () => {
  const [text] = useState('## md-editor-rt\n\n');
  const mdEditorRef = useRef<ExposeParam>();
  useEffect(() => {
    bus.on('insertMarkBlock', () => {
      mdEditorRef.current?.insert((selectedText) => {
        return {
          targetValue: `==${selectedText}==`,
          select: true,
          deviationStart: 2,
          deviationEnd: -2
        };
      });
    });
  }, []);
  return <MdEditor modelValue={text} ref={mdEditorRef} />;
};Attach: Simple version of EventBus
            
            
              ts
              
              
              
            
          
          /* eslint-disable @typescript-eslint/ban-types */
class EventBus {
  private events: Map<string, Function[]>;
  constructor() {
    this.events = new Map();
  }
  on(eventName: string, fn: Function) {
    if (!eventName) {
      console.error('Get a wrong eventName');
      return false;
    }
    if (!(fn instanceof Function)) {
      console.error('Get a wrong callback');
      return false;
    }
    const fns = this.events.get(eventName) || [];
    fns.push(fn);
    this.events.set(eventName, fns);
  }
  emit(eventName: string, ...args: any[]) {
    this.events.get(eventName)?.forEach((fn) => {
      fn(args);
    });
  }
}
export default new EventBus();🍦 Change Theme
Themes are divided into editor theme(theme), article preview theme(previewTheme) and code theme(codeTheme).
🍧 Editor Theme
Support light and dark default.
            
            
              jsx
              
              
              
            
          
          import { useState } from 'react';
import { MdEditor } from 'md-editor-rt';
import 'md-editor-rt/lib/style.css';
export default () => {
  const [text, setText] = useState('hello md-editor-rt!');
  const [theme] = useState('dark');
  return <MdEditor modelValue={text} onChange={setText} theme={theme} />;
};🍡 Preview Theme
There are 6 kinds of themes: default, github, vuepress, mk-cute, smart-blue and cyanosis. It is useful When you want to show your article directly. Modify previewTheme.
            
            
              jsx
              
              
              
            
          
          import { useState } from 'react';
import { MdEditor } from 'md-editor-rt';
import 'md-editor-rt/lib/style.css';
export default () => {
  const [text, setText] = useState('hello md-editor-rt!');
  const [previewTheme] = useState('github');
  return <MdEditor modelValue={text} onChange={setText} previewTheme={previewTheme} />;
};- 
Custom - Write cssunder thexxx-themeclaa.xxxis the name of your theme, for more examples, refer to markdown-theme.
 xxx.css css.xxx-theme code { color: red; }- Import
 jsimport 'xxx.css';- Set previewTheme
 jsx<MdEditor previewTheme="xxx" />
- Write 
🎄 Code Theme
There are 8 kinds of themes: atom, a11y, github, gradient, kimbie, paraiso,qtcreator and stackoverflow, they are all from highlight.js.
- 
Usage jsximport { useState } from 'react'; import { MdEditor } from 'md-editor-rt'; import 'md-editor-rt/lib/style.css'; export default () => { const [text, setText] = useState('hello md-editor-rt!'); const [codeTheme] = useState('atom'); return <MdEditor modelValue={text} onChange={setText} codeTheme={codeTheme} />; };
- 
Custom - Find or Write your favorite theme, then config them:
 jsimport { config } from 'md-editor-rt'; config({ editorExtensions: { highlight: { css: { xxxxx: { light: 'https://unpkg.com/highlight.js@11.2.0/styles/xxxxx-light.css', dark: 'https://unpkg.com/highlight.js@11.2.0/styles/xxxxx-dark.css' }, yyyyy: { light: 'https://unpkg.com/highlight.js@11.2.0/styles/xxxxx-light.css', dark: 'https://unpkg.com/highlight.js@11.2.0/styles/xxxxx-dark.css' } } } } });If some keys in object cssare same as Editor's, Editor's whill be replaced. 2. SetcodeThemejsx<MdEditor codeTheme="xxxxx" />
🛠 Config Extensions
Extensions highlight, prettier, cropper, screenfull are import from cdn. When your project is running offline, replace urls of these extensions. Some Extensions support be injected in development environment.
Example for screenfull:
⚰️ Inject Directly
            
            
              jsx
              
              
              
            
          
          import { useState } from 'react';
import { MdEditor, config } from 'md-editor-rt';
import 'md-editor-rt/lib/style.css';
import screenfull from 'screenfull';
config({
  editorExtensions: {
    screenfull: {
      instance: screenfull
    }
  }
});
export default () => {
  const [text, setText] = useState('hello md-editor-rt!');
  return <MdEditor modelValue={text} onChange={setText} />;
};📡 Intranet Link
Get files from unpkg.com.
            
            
              jsx
              
              
              
            
          
          import { useState } from 'react';
import { MdEditor, config } from 'md-editor-rt';
import 'md-editor-rt/lib/style.css';
config({
  editorExtensions: {
    screenfull: {
      js: 'https://localhost:8090/screenfull@5.2.0/index.js'
    }
  }
});
export default () => {
  const [text, setText] = useState('hello md-editor-rt!');
  return <MdEditor modelValue={text} onChange={setText} />;
};📷 Upload Pictures
By default, you can select multiple pictures. You can paste and upload screenshots and copy web page pictures.
Tips: When pasting pictures, if they are GIF graphs, it does not work! Please upload it by file system.
            
            
              jsx
              
              
              
            
          
          import { useState } from 'react';
import { MdEditor } from 'md-editor-rt';
import 'md-editor-rt/lib/style.css';
const onUploadImg = async (files, callback) => {
  const res = await Promise.all(
    files.map((file) => {
      return new Promise((rev, rej) => {
        const form = new FormData();
        form.append('file', file);
        axios
          .post('/api/img/upload', form, {
            headers: {
              'Content-Type': 'multipart/form-data'
            }
          })
          .then((res) => rev(res))
          .catch((error) => rej(error));
      });
    })
  );
  callback(res.map((item) => item.data.url));
};
export default () => {
  const [text, setText] = useState('hello md-editor-rt!');
  return <MdEditor modelValue={text} onChange={setText} onUploadImg={onUploadImg} />;
};🏳️🌈 Extension Language
            
            
              js
              
              
              
            
          
          import { useState } from 'react';
import { MdEditor, config } from 'md-editor-rt';
import 'md-editor-rt/lib/style.css';
config({
  editorConfig: {
    languageUserDefined: {
      'my-lang': {
        toolbarTips: {
          bold: 'bold',
          underline: 'underline',
          italic: 'italic',
          strikeThrough: 'strikeThrough',
          title: 'title',
          sub: 'subscript',
          sup: 'superscript',
          quote: 'quote',
          unorderedList: 'unordered list',
          orderedList: 'ordered list',
          task: 'task list',
          codeRow: 'inline code',
          code: 'block-level code',
          link: 'link',
          image: 'image',
          table: 'table',
          mermaid: 'mermaid',
          katex: 'formula',
          revoke: 'revoke',
          next: 'undo revoke',
          save: 'save',
          prettier: 'prettier',
          pageFullscreen: 'fullscreen in page',
          fullscreen: 'fullscreen',
          preview: 'preview',
          htmlPreview: 'html preview',
          catalog: 'catalog',
          github: 'source code'
        },
        titleItem: {
          h1: 'Lv1 Heading',
          h2: 'Lv2 Heading',
          h3: 'Lv3 Heading',
          h4: 'Lv4 Heading',
          h5: 'Lv5 Heading',
          h6: 'Lv6 Heading'
        },
        imgTitleItem: {
          link: 'Add Img Link',
          upload: 'Upload Img',
          clip2upload: 'Clip Upload'
        },
        linkModalTips: {
          linkTitle: 'Add Link',
          imageTitle: 'Add Image',
          descLabel: 'Desc:',
          descLabelPlaceHolder: 'Enter a description...',
          urlLabel: 'Link:',
          urlLabelPlaceHolder: 'Enter a link...',
          buttonOK: 'OK'
        },
        clipModalTips: {
          title: 'Crop Image',
          buttonUpload: 'Upload'
        },
        copyCode: {
          text: 'Copy',
          successTips: 'Copied!',
          failTips: 'Copy failed!'
        },
        mermaid: {
          flow: 'flow',
          sequence: 'sequence',
          gantt: 'gantt',
          class: 'class',
          state: 'state',
          pie: 'pie',
          relationship: 'relationship',
          journey: 'journey'
        },
        katex: {
          inline: 'inline',
          block: 'block'
        },
        footer: {
          markdownTotal: 'Word Count',
          scrollAuto: 'Scroll Auto'
        }
      }
    }
  }
});
export default () => {
  const [text, setText] = useState('hello md-editor-rt!');
  const [language] = useState('my-lang');
  return <MdEditor modelValue={text} onChange={setText} language={language} />;
};You can install the existing language also: md-editor-extension. Refer to extension library for the usage and the way to contribute~
📄 Get Catalogue
- 
Get jsximport { useState } from 'react'; import { MdEditor } from 'md-editor-rt'; import 'md-editor-rt/lib/style.css'; export default () => { const [text, setText] = useState('hello md-editor-rt!'); const [catalogList, setList] = useState([]); return <MdEditor modelValue={text} onChange={setText} onGetCatalog={setList} />; };
- 
Display Use MdCatalogjsximport { useState } from 'react'; import { MdPreview, MdCatalog } from 'md-editor-rt'; import 'md-editor-rt/lib/preview.css'; const editorId = 'my-editor'; export default () => { const [state] = useState({ text: '# heading', scrollElement: document.documentElement }); return ( <> <MdPreview modelValue={state.text} id={editorId} /> <MdCatalog editorId={editorId} scrollElement={state.scrollElement} /> </> ); };
🪚 Define Toolbar
after v1.2.0, You can sort the toolbar as you like, split tools by
'-', the left and right toolbars are divided by'='!
            
            
              jsx
              
              
              
            
          
          import { useState } from 'react';
import { MdEditor } from 'md-editor-rt';
import 'md-editor-rt/lib/style.css';
export default () => {
  const [text, setText] = useState('hello md-editor-rt!');
  const [toolbars] = useState(['italic', 'underline', '-', 'bold', '=', 'github']);
  return <MdEditor modelValue={text} onChange={setText} toolbars={toolbars} />;
};💪 Customize Toolbar
There are examples of mark and emoji.
To get complete code, refer to docs.

Get more emojis, go to https://getemoji.com/.
🧙♂️ Change Styles
            
            
              less
              
              
              
            
          
          .css-vars(@isDark) {
  --md-color: if(@isDark, #999, #222);
  --md-hover-color: if(@isDark, #bbb, #000);
  --md-bk-color: if(@isDark, #000, #fff);
  --md-bk-color-outstand: if(@isDark, #333, #f2f2f2);
  --md-bk-hover-color: if(@isDark, #1b1a1a, #f5f7fa);
  --md-border-color: if(@isDark, #2d2d2d, #e6e6e6);
  --md-border-hover-color: if(@isDark, #636262, #b9b9b9);
  --md-border-active-color: if(@isDark, #777, #999);
  --md-modal-mask: #00000073;
  --md-scrollbar-bg-color: if(@isDark, #0f0f0f, #e2e2e2);
  --md-scrollbar-thumb-color: if(@isDark, #2d2d2d, #0000004d);
  --md-scrollbar-thumb-hover-color: if(@isDark, #3a3a3a, #00000059);
  --md-scrollbar-thumb-active-color: if(@isDark, #3a3a3a, #00000061);
}
.md-editor {
  .css-vars(false);
}
.md-editor-dark {
  .css-vars(true);
}Change background color in dark mode:
            
            
              css
              
              
              
            
          
          .md-editor-dark {
  --md-bk-color: #333 !important;
}🙍🏻♂️ Import All Library
- Install Dependencies
            
            
              shell
              
              
              
            
          
          yarn add screenfull katex cropperjs@1 mermaid highlight.js prettierCurrently cropperjs@2 is being optimised, and there are no plans to switch to version 2.0. !!!
- Configure
We recommend configuring it at the project entry point, such as in main.js for projects created with Vite. Avoid calling config within components!
main.js
            
            
              js
              
              
              
            
          
          import { config } from 'md-editor-rt';
import 'md-editor-rt/lib/style.css';
import screenfull from 'screenfull';
import katex from 'katex';
import 'katex/dist/katex.min.css';
import Cropper from 'cropperjs';
import 'cropperjs/dist/cropper.css';
import mermaid from 'mermaid';
import highlight from 'highlight.js';
import 'highlight.js/styles/atom-one-dark.css';
// <3.0
import prettier from 'prettier';
import parserMarkdown from 'prettier/parser-markdown';
// >=3.0
import * as prettier from 'prettier';
import parserMarkdown from 'prettier/plugins/markdown';
config({
  editorExtensions: {
    prettier: {
      prettierInstance: prettier,
      parserMarkdownInstance: parserMarkdown
    },
    highlight: {
      instance: highlight
    },
    screenfull: {
      instance: screenfull
    },
    katex: {
      instance: katex
    },
    cropper: {
      instance: Cropper
    },
    mermaid: {
      instance: mermaid
    }
  }
});
            
            
              jsx
              
              
              
            
          
          import { MdEditor } from 'md-editor-rt';
import 'md-editor-rt/lib/style.css';
export default () => {
  return <MdEditor modelValue="" />;
};Tips: While import highlight styles by yourself, editor will not be able to change code styles.
🔒 Handling XSS at Compile Time
Version 5.0 exports the built-in XSS plugin, which is no longer added by default. The exported XSS plugin includes additional tags and attributes on top of the default whitelist:
            
            
              json
              
              
              
            
          
          {
  "img": ["class"],
  // Task List
  "input": ["class", "disabled", "type", "checked"],
  // Embedded video codes such as YouTube, WeTV, and Bilibili
  "iframe": [
    "class",
    "width",
    "height",
    "src",
    "title",
    "border",
    "frameborder",
    "framespacing",
    "allow",
    "allowfullscreen"
  ]
}🔒 Add XSS extension
            
            
              js
              
              
              
            
          
          import { config, XSSPlugin } from 'md-editor-rt';
config({
  markdownItPlugins(plugins) {
    return [
      ...plugins,
      {
        type: 'xss',
        plugin: XSSPlugin,
        options: {}
      }
    ];
  }
});🔏 Modify XSS configuration
Add a configuration that allows for events where image loading fails
            
            
              js
              
              
              
            
          
          import { config, XSSPlugin } from 'md-editor-rt';
// import { getDefaultWhiteList } from 'xss';
config({
  markdownItPlugins(plugins) {
    return [
      ...plugins,
      {
        type: 'xss',
        plugin: XSSPlugin,
        options: {
          // Option 1: Extend All by Yourself
          // xss() {
          //   return {
          //     whiteList: Object.assign({}, getDefaultWhiteList(), {
          //       // If you need to use task list, please keep this configuration
          //       img: ['class'],
          //       input: ['class', 'disabled', 'type', 'checked'],
          //       // If you need to use embedded video code, please keep this configuration
          //       iframe: [
          //         'class',
          //         'width',
          //         'height',
          //         'src',
          //         'title',
          //         'border',
          //         'frameborder',
          //         'framespacing',
          //         'allow',
          //         'allowfullscreen'
          //       ],
          //       img: ['onerror']
          //     })
          //   };
          // }
          // Option 2: Add on Top of the Default Whitelist. ^4.15.6
          extendedWhiteList: {
            img: ['onerror']
          }
        }
      }
    ];
  }
});More configuration references: js-xss
🔒 Handling XSS after Compilation
Using sanitize to sanitize html. eg: sanitize-html
            
            
              shell
              
              
              
            
          
          yarn add sanitize-html
            
            
              jsx
              
              
              
            
          
          import { MdEditor } from 'md-editor-rt';
import 'md-editor-rt/lib/style.css';
import sanitizeHtml from 'sanitize-html';
const sanitize = (html) => sanitizeHtml(html);
export default () => {
  return <MdEditor sanitize={sanitize} />;
};🗂 Folding Document Content
            
            
              js
              
              
              
            
          
          import { config } from 'md-editor-rt';
import { foldGutter } from '@codemirror/language';
import { lineNumbers } from '@codemirror/view';
config({
  codeMirrorExtensions(extensions) {
    return [
      ...extensions,
      {
        type: 'lineNumbers',
        extension: lineNumbers()
      },
      {
        type: 'foldGutter',
        extension: foldGutter()
      }
    ];
  }
});🏄🏻♂️ Open Links In New Window
- Install additional extensions
            
            
              shell
              
              
              
            
          
          yarn add markdown-it-link-attributes- Add extensions to the compiler
            
            
              js
              
              
              
            
          
          import { config } from 'md-editor-rt';
import LinkAttr from 'markdown-it-link-attributes';
// import Anchor from 'markdown-it-anchor';
config({
  markdownItPlugins(plugins) {
    return [
      ...plugins,
      {
        type: 'linkAttr',
        plugin: LinkAttr,
        options: {
          matcher(href: string) {
            // If markdown-it-anchor is used.
            // Anchor links at the heading should be ignored.
            return !href.startsWith('#');
          },
          attrs: {
            target: '_blank',
          },
        },
      },
      // {
      //   type: 'anchor',
      //   plugin: Anchor,
      //   options: {
      //     permalink: Anchor.permalink.headerLink(),
      //     slugify(s: string) {
      //       return s;
      //     }
      //   }
      // }
    ];
  },
});☑️ Toggleable status task list
            
            
              js
              
              
              
            
          
          import { config } from 'md-editor-rt';
config({
  markdownItPlugins(plugins, { editorId }) {
    return plugins.map((item) => {
      if (item.type === 'taskList') {
        return {
          ...item,
          options: {
            ...item.options,
            enabled: true
            // If you just want to enable this feature for a certain editor
            // enabled: editorId === 'myId'
          }
        };
      }
      return item;
    });
  }
});
            
            
              jsx
              
              
              
            
          
          <MdEditor id="myId" modelValue={text} onChange={setText} />🎳 co-working
Install yjs
            
            
              shell
              
              
              
            
          
          npm i yjs y-codemirror.next y-websocketAdd the yjs extension in editorConfig.ts:
            
            
              ts
              
              
              
            
          
          import { config } from 'md-editor-rt';
import { Compartment } from '@codemirror/state';
import * as Y from 'yjs';
import { yCollab } from 'y-codemirror.next';
import { WebsocketProvider } from 'y-websocket';
import * as random from 'lib0/random';
const usercolors = [
  { color: '#30bced', light: '#30bced33' },
  { color: '#6eeb83', light: '#6eeb8333' },
  { color: '#ffbc42', light: '#ffbc4233' },
  { color: '#ecd444', light: '#ecd44433' },
  { color: '#ee6352', light: '#ee635233' },
  { color: '#9ac2c9', light: '#9ac2c933' },
  { color: '#8acb88', light: '#8acb8833' },
  { color: '#1be7ff', light: '#1be7ff33' }
];
export const yjsCompartment = new Compartment();
let currentProvider: WebsocketProvider | null = null;
let currentDoc: Y.Doc | null = null;
export const cleanupYjs = () => {
  currentProvider?.destroy();
  currentDoc?.destroy();
  currentProvider = null;
  currentDoc = null;
};
export const createYjsExtension = (roomId: string) => {
  cleanupYjs();
  const userColor = usercolors[random.uint32() % usercolors.length];
  const ydoc = new Y.Doc();
  const provider = new WebsocketProvider('ws://127.0.0.1:1234/ws', roomId, ydoc);
  const ytext = ydoc.getText('module-name');
  const undoManager = new Y.UndoManager(ytext);
  provider.awareness.setLocalStateField('user', {
    name: 'Anonymous ' + Math.floor(Math.random() * 100),
    color: userColor.color,
    colorLight: userColor.light
  });
  currentDoc = ydoc;
  currentProvider = provider;
  return yCollab(ytext, provider.awareness, { undoManager });
};
config({
  codeMirrorExtensions(extensions) {
    return [
      ...extensions,
      {
        type: 'yjs',
        extension: yjsCompartment.of([])
      }
    ];
  }
});If you want to use it in only one editor, try distinguishing using editorId (^4.20.0):
            
            
              js
              
              
              
            
          
          config({
  codeMirrorExtensions(extensions, { editorId }) {
    return editorId === 'myId'
      ? [
          ...extensions,
          {
            type: 'yjs',
            extension: yjsCompartment.of([])
          }
        ]
      : extensions;
  }
});MyEditor.tsx
            
            
              tsx
              
              
              
            
          
          import React, { useCallback, useEffect, useId, useRef, useState } from 'react';
import { MdEditor, ExposeParam } from 'md-editor-rt';
import { createYjsExtension, yjsCompartment, cleanupYjs } from './extendEditor';
const MyEditor = () => {
  const [roomId, setRoomId] = useState('default-room');
  const [text, setText] = useState('');
  const editorRef = useRef<ExposeParam>(null);
  const id = useId();
  const updateEditorExtension = useCallback((room: string) => {
    const view = editorRef.current?.getEditorView();
    if (view) {
      const extension = createYjsExtension(room);
      view.dispatch({
        effects: yjsCompartment.reconfigure(extension)
      });
    }
  }, []);
  useEffect(() => {
    updateEditorExtension(roomId);
  }, [roomId, updateEditorExtension]);
  useEffect(() => {
    return cleanupYjs;
  }, []);
  return (
    <div>
      <input value={roomId} onChange={(e) => setRoomId(e.target.value)} />
      <MdEditor id={id} value={text} onChange={setText} ref={editorRef} />
    </div>
  );
};
export default MyEditor;The code above only shows how a single editor switches rooms, so if there are multiple editors on a single page and you need to switch rooms, please explore on your own.
📝 Extend code block tool
            
            
              js
              
              
              
            
          
          config({
  markdownItPlugins(plugins, { editorId }) {
    return plugins.map((item) => {
      switch (item.type) {
        case 'code': {
          return {
            ...item,
            options: {
              ...item.options,
              extraTools: '<span class="extra-code-tools">Additional features</span>'
              // or
              // extraTools: ({ lang }) => '<span class="extra-code-tools">Additional features</span>',
            }
          };
        }
        default: {
          return item;
        }
      }
    });
  }
});It will be displayed after the 'Copy Code' button. By working together with the onRemount event, you can correctly retrieve these elements using querySelectorAll and bind event listeners to them.
Here is an example of how to print code:
            
            
              js
              
              
              
            
          
          const onRemount = useCallback(() => {
  document.querySelectorAll(`#${editorId} .${prefix}-preview .${prefix}-code`).forEach((codeBlock: Element) => {
    const tools = codeBlock.querySelectorAll('.extra-code-tools');
    tools.forEach((item) => {
      item.addEventListener('click', (e) => {
        e.preventDefault();
        const activeCode = codeBlock.querySelector('input:checked + pre code') || codeBlock.querySelector('pre code');
        const codeText = activeCode?.textContent;
        console.log(codeText);
      });
    });
  });
}, []);