Markdown & Conversion

Supporting Markdown lets power users write formatting quickly. Learn how to convert between Markdown and your document model.

Overview

Many modern editors (Notion, Slack, Discord) support "Markdown Shortcuts"—typing # creates a heading, **text** makes it bold. Full Markdown import/export is also a common requirement.

Markdown to Model

To import Markdown, you need a parser like marked or remark, then transform the AST into your model.

import { marked } from 'marked';

function importMarkdown(md) {
  const tokens = marked.lexer(md);
  const doc = { type: 'doc', children: [] };
  
  tokens.forEach(token => {
    if (token.type === 'heading') {
      doc.children.push({
        type: 'heading',
        attrs: { level: token.depth },
        content: token.text
      });
    }
    // ... handle other tokens
  });
  
  return doc;
}

Model to Markdown

To export key-value models to Markdown, implement a serializer that recurses through your node tree.

function serialize(node) {
  if (node.type === 'text') return node.text;
  
  if (node.type === 'paragraph') {
    return node.children.map(serialize).join('') + '\n\n';
  }
  
  if (node.type === 'heading') {
    return '#'.repeat(node.attrs.level) + ' ' + 
           node.children.map(serialize).join('') + '\n\n';
  }
  
  // ... handle other types
}

Markdown Shortcuts

For auto-formatting (e.g., typing * lists), listen to input or beforeinput events and check the text preceding the cursor.

  • Check for trigger characters (space, enter).
  • Regex match the line content (e.g., /^#\s/).
  • Delete the markdown characters and change the block type.

Export Options

Beyond Markdown, considering supporting:

  • HTML: Standard serialization (see HTML Mapping).
  • Plain Text: Strip all tags and block structure.
  • PDF: Use window.print() CSS media queries or server-side generation (Puppeteer).