milkdown

Plugin-driven, WYSIWYG Markdown editor for React with a clean prose editing experience.

milkdown

React

Plugin-driven, WYSIWYG Markdown editor for React with a clean prose editing experience.

Fit

Bundle (gzip)

92.1 kB

307.7 kB raw

Install size includes transitive dependencies. Bundle size is gzipped browser payload.

Freshness

The Problem

Adding a rich text editor to a React app that outputs clean Markdown is surprisingly difficult:

  • Most WYSIWYG editors (Quill, TinyMCE, Draft.js) output HTML — not Markdown
  • Markdown-outputting editors often look plain or require a lot of custom styling
  • Building a custom editor from scratch with ProseMirror is complex and time-consuming
  • Many editors are abandoned or have poor TypeScript support

What It Does

Milkdown is a plugin-driven Markdown WYSIWYG editor built on top of ProseMirror and Remark. You write in a live-preview prose environment and the output is always clean Markdown. The plugin architecture means you can add tables, math formulas, slash commands, collaborative editing, and more — only loading what you need.

Installation

bash
npm install @milkdown/core @milkdown/react @milkdown/preset-commonmark @milkdown/theme-nord

Usage Example

Basic editor in React

tsx
import { Editor, rootCtx, defaultValueCtx } from "@milkdown/core";
import { nord } from "@milkdown/theme-nord";
import { ReactEditor, useEditor } from "@milkdown/react";
import { commonmark } from "@milkdown/preset-commonmark";

import "@milkdown/theme-nord/style.css";

export function MarkdownEditor() {
  const { get } = useEditor((root) =>
    Editor.make()
      .config((ctx) => {
        ctx.set(rootCtx, root);
        ctx.set(defaultValueCtx, "# Hello Milkdown\n\nStart typing here...");
      })
      .use(nord)
      .use(commonmark)
  );

  return <ReactEditor editor={get()} />;
}

Reading the Markdown output

tsx
import { getMarkdown } from "@milkdown/utils";

function SaveButton({ editorRef }) {
  function handleSave() {
    const markdown = editorRef.get()?.action(getMarkdown());
    console.log(markdown); // clean Markdown string
    // save to your database
  }

  return <button onClick={handleSave}>Save</button>;
}

Adding table support

tsx
import { gfm } from "@milkdown/preset-gfm";

Editor.make()
  .config(...)
  .use(nord)
  .use(commonmark)
  .use(gfm) // adds tables, strikethrough, task lists
  .create();

Related packages