— MIT Licensed — 406+ tests passing

The embeddable markdown editor
your app deserves.

Drop-in Markdown editing with KaTeX math, Markmap mindmaps, syntax-highlighted source, @mentions, document link cards, task lists, and admonitions. Built on CodeMirror 6 for buttery performance. React or vanilla JS.

$npm install @powerduck/md-editor
React 17+Vanilla JSTypeScriptLight / DarkSimple / Complex modes

Features

Built for real-world writing.

Every primitive you need for production markdown workflows — math, mindmaps, mentions, code, cards — without the bloat of a full CMS.

KaTeX Math

Inline $…$ and block $$…$$ formulas rendered with KaTeX. Zero config.

Markmap Mindmaps

Turn fenced ```mindmap blocks into interactive, zoomable mindmaps.

@Mentions

Type @ to trigger a searchable user picker. Async data, keyboard nav, rendered as chips.

Document Link Cards

Type / to insert links that auto-fetch OG metadata and render as rich preview cards (thumbnail + title + description), with graceful fallback to plain links.

📄
Getting Started Guide
Quick start for embedding the editor in your app.

Code Blocks

highlight.js with traffic-light chrome, one-click copy, theme-aware colors. 20+ languages.

Task Lists

GFM checkboxes with green-for-done styling. Toggle from toolbar or keyboard.

Admonition Blocks

:::warning, :::info, :::tip, :::danger and more.

Incremental Rendering

Block-level diffing re-renders only what changed. Large docs stay smooth at 60fps.

Configurable Toolbar & Modes

Show, hide, relabel, or replace any toolbar button with your own icon (ReactNode). Simple mode strips to essentials. 11+ keyboard shortcuts with a Help dialog.

HeadingBoldItalicLinkImageVideoYouTubeTableMathMindmapCodeQuoteListsTaskTips

Image Upload Hook

Paste or drop images. onImageUpload returns the URL to insert.

Standalone renderMarkdown

Render markdown anywhere with the same math, mindmap, code, and card styling — no editor needed.

Live Demo

See it in action.

Four common workflows, rendered exactly as they appear in your app.

~/project/README.md

Quick Start

Up and running in under a minute.

Install, import, render. Six steps covering every integration pattern.

1

Install

bash
npm install @powerduck/md-editor
2

Import styles

css

One CSS file covers editor, preview, code blocks, math, and mindmaps.

import "@powerduck/md-editor/dist/style.css";
3

React component

tsx
import { useRef } from "react";
import {
  MarkdownEditorReact,
  type MarkdownEditorHandle,
} from "@powerduck/md-editor/react";

export function App() {
  const ref = useRef<MarkdownEditorHandle>(null);
  return (
    <MarkdownEditorReact
      ref={ref}
      defaultValue="# Hello world"
      mode="complex"
      theme="light"
      onChange={(v) => console.log(v.length)}
    />
  );
}
4

Vanilla JS

ts
import { MarkdownEditor } from "@powerduck/md-editor";

const editor = new MarkdownEditor(container, {
  value: "# Hello",
  mode: "simple",
  theme: "dark",
});
5

Standalone render

ts

Need read-only markdown? Use the same renderer that powers the preview pane.

import { renderMarkdown } from "@powerduck/md-editor";

// Math, mindmaps, code, admonitions, cards
const html = renderMarkdown("# Title\n\n$E=mc^2$");
container.innerHTML = html;
6

Mentions & doc links

tsx
<MarkdownEditorReact
  defaultValue="# Note"
  mention={{
    onMentionSearch: async (q) =>
      users.filter(u => u.label.includes(q)),
    minChars: 0,
  }}
  docLink={{
    onDocSearch: async (q) =>
      docs.filter(d => d.title.includes(q)),
    insertStyle: "auto",
  }}
/>

API Reference

A small, predictable surface.

Every option is optional. Sensible defaults get you started; full configuration takes over when you need it.

Prop Type Default Description
defaultValue string "" Initial markdown content.
value string — Controlled value. When provided, the editor becomes controlled.
mode "simple" | "complex" "complex" Simple hides toolbar and status bar for a minimal writing experience.
theme "light" | "dark" "light" Color theme for editor, preview, and code blocks. Switch at runtime via setTheme().
onChange (md: string) => void — Fired on every document change (debounced via CodeMirror's update listener).
placeholder string — Placeholder text shown when the editor is empty.
lineNumbers boolean false Show line numbers in the source pane.
autoPreview boolean true Automatically render the preview pane on change.
toolbar ToolbarConfig all buttons Show/hide/relabel/replace any toolbar button with a custom icon (ReactNode).
mention MentionOptions disabled @mention picker with async search (onMentionSearch), custom rendering, keyboard navigation, and onMentionSelect hook.
docLink DocLinkOptions disabled /slash document picker with async search (onDocSearch), OG-metadata fetch hook (onFetchDocMeta), and insertStyle ("auto" | "card" | "link").
onImageUpload (file: File) => Promise<string> — Hook for paste/drop image uploads. Return the resolved URL to insert.
includeExamples boolean false Include example values in generated JSON schemas and preview.
Instance Methods
editor.getValue()       // => string
editor.setValue(md)     // => void
editor.setTheme("dark")  // => void
editor.setMode("simple") // => void
editor.focus()         // => void
editor.destroy()       // => void
Keyboard Shortcuts
BoldCtrl/Cmd + B
ItalicCtrl/Cmd + I
Insert linkCtrl/Cmd + K
Inline codeCtrl/Cmd + E
BlockquoteCtrl/Cmd + .
Code blockCtrl/Cmd + /
Task listAlt + T
Math formulaAlt + M
Help dialogCtrl/Cmd + /

FAQ

Questions, answered.

Yes. The React component supports React 17+ as a peer dependency. It also works with React 18 concurrent features. Vanilla JS users don't need React at all.
The core editor is ~50KB gzipped. CodeMirror, KaTeX, Markmap, and highlight.js are peer/external dependencies so your bundler can share them with the rest of your app. Incremental rendering keeps large documents smooth.
Absolutely. Pass a toolbar config to show/hide individual buttons, relabel them, or replace their icons with your own ReactNode. Simple mode strips the toolbar entirely for a distraction-free writing experience.
Type @ to trigger a user picker (async search via onMentionSearch). Type / for document links (onDocSearch). Both support full keyboard navigation and Enter-to-insert. Mentions render as chips; doc links render as preview cards with auto-fetched OG metadata.
The renderer uses markdown-it with sane defaults. Raw HTML is escaped by default. Math and mindmap blocks are rendered in isolated containers. For production use, always sanitize user-generated content at your application layer.
Yes. The standalone renderMarkdown() function renders markdown to HTML with the same math, mindmap, code, admonition, and card styling — perfect for read-only views, comments, or chat messages.

Start writing beautiful markdown today.

One install. Zero config. Everything your app needs.

Get StartedView on GitHub