Skip to content

xcancloud/Markdown

Repository files navigation

Markdown

npm version License: MIT React 18+

Production-grade, extensible Markdown rendering and editing for React — CommonMark & GFM, math, Mermaid, Shiki highlighting, CodeMirror editor, themes, and i18n in one package.

English · 简体中文 · Repository · npm


Markdown Editor — toolbar, split preview with syntax highlighting, math, tables, task lists, alerts, and TOC sidebar

Table of Contents

Features

  • CommonMark & GFM — Tables, task lists, strikethrough, footnotes, autolinks
  • Syntax Highlighting — 30+ languages via Shiki (VS Code–quality themes)
  • Math — Inline and block KaTeX ($...$, $$...$$)
  • Mermaid — Flowcharts, sequence, Gantt, class diagrams (lazy client render)
  • SVG Preview — Fenced ```svg or ```xml with SVG content renders as a sanitized inline preview (copy / download when not streaming)
  • Rich Editor — CodeMirror 6, toolbar, split/tabs layouts, image paste/drop, auto-save, shortcuts
  • Code Block UX — Copy, download (language-based extension; optional file: / comment meta for filename), HTML sandbox preview
  • GFM Alerts & Containers> [!NOTE] / > [!WARNING] and :::tip / :::warning directives
  • TOC Sidebar — Auto-generated outline with active heading tracking (MarkdownRenderer)
  • Front Matter — YAML (and TOML) metadata via remark-frontmatter
  • Emoji:smile: shortcodes (remark-emoji)
  • Security — rehype-sanitize schema, URL handling, XSS-oriented defaults
  • Accessibility — rehype a11y helpers, ARIA-oriented output
  • Streamingstreaming prop for live SSE/chunked content (debounce bypass, cursor affordance)
  • Themes — Light / Dark / Auto + CSS variables; presets: GitHub (default), Notion, Typora
  • i18nen-US and zh-CN built-in
  • Dual Build — ESM + CJS, TypeScript declarations, tree-shakeable entry

Quick Start

Installation

npm install @xcan-cloud/markdown

Peer dependencies:

npm install react react-dom

Import styles once in your app:

import '@xcan-cloud/markdown/styles';

This single import includes both the renderer and editor styles. Optional theme presets are imported separately (see Customization).

Basic Rendering

import { MarkdownRenderer } from '@xcan-cloud/markdown';
import '@xcan-cloud/markdown/styles';

function App() {
  return <MarkdownRenderer source="# Hello\n\nThis is **Markdown**." />;
}

Editor (split view)

import { MarkdownEditor } from '@xcan-cloud/markdown';
import '@xcan-cloud/markdown/styles';

function App() {
  return (
    <MarkdownEditor
      initialValue="# Start editing…"
      layout="split"
      onChange={(value) => console.log(value)}
    />
  );
}

Theme & Locale Provider

import {
  MarkdownProvider,
  MarkdownEditor,
  ThemeSwitcher,
  LocaleSwitcher,
} from '@xcan-cloud/markdown';
import '@xcan-cloud/markdown/styles';

function App() {
  return (
    <MarkdownProvider defaultTheme="auto" defaultLocale="en-US">
      <div style={{ display: 'flex', gap: 8, marginBottom: 16 }}>
        <ThemeSwitcher />
        <LocaleSwitcher />
      </div>
      <MarkdownEditor initialValue="# Hello" layout="split" />
    </MarkdownProvider>
  );
}

SSR-Friendly Viewer (no CodeMirror)

import { MarkdownViewer } from '@xcan-cloud/markdown';
import '@xcan-cloud/markdown/styles';

function Page({ markdown }: { markdown: string }) {
  return <MarkdownViewer source={markdown} theme="light" />;
}

API Reference

<MarkdownRenderer />

Full-featured renderer: TOC, Mermaid/SVG post-processing, code actions, streaming.

Prop Type Default Description
source string Markdown source
options ProcessorOptions Unified pipeline options
className string '' Root element class
theme 'light' | 'dark' | 'auto' from context / 'auto' Color mode
showToc boolean true Show TOC sidebar
tocPosition 'left' | 'right' 'right' TOC placement
debounceMs number 150 Render debounce (disabled while streaming)
onRendered (info: { html: string; toc: TocItem[] }) => void After successful render
onLinkClick (href: string, event: MouseEvent) => void Link click hook
onImageClick (src: string, alt: string, event: MouseEvent) => void Image click hook
components Partial<Record<string, ComponentType<any>>> Custom HTML tag mapping
streaming boolean false Live stream mode
onStreamEnd () => void Fired when streaming goes truefalse
height string Fixed height of the renderer container
minHeight string Minimum height of the renderer container
maxHeight string Maximum height of the renderer container

<MarkdownEditor />

Extends renderer props except source is replaced by editor value APIs.

Prop Type Default Description
initialValue string '' Initial markdown
value string Controlled value
onChange (value: string) => void Content change
layout LayoutMode 'split' split | tabs | editor-only | preview-only
minHeight / maxHeight string Editor area sizing
toolbar ToolbarConfig default set false to hide, or item list
readOnly boolean false Read-only editor
onImageUpload (file: File) => Promise<string> Return URL for pasted/dropped images
onAutoSave (value: string) => void Periodic save callback
autoSaveInterval number 30000 Auto-save interval (ms)
extensions Extension[] [] Extra CodeMirror extensions
shortcuts ShortcutMap Custom keymap handlers
maxLength number Hard limit + counter UI

All MarkdownRenderer props except source also apply to the preview pane (e.g. options, theme, showToc).

<MarkdownViewer />

Lightweight viewer using useMarkdown (no CodeMirror).

Prop Type Default Description
source string Markdown source
options ProcessorOptions Pipeline options
className string '' Root class
theme 'light' | 'dark' | 'auto' from context Theme
onRendered (info: { html: string; toc: TocItem[] }) => void Note: toc is [] in viewer
height string Fixed height of the viewer container
minHeight string Minimum height of the viewer container
maxHeight string Maximum height of the viewer container

<MarkdownProvider />

Prop Type Default Description
children ReactNode App subtree
defaultTheme 'light' | 'dark' | 'auto' 'auto' Initial theme
defaultLocale 'en-US' | 'zh-CN' 'en-US' Initial locale

<ThemeSwitcher /> / <LocaleSwitcher />

Optional controls; read/write theme and locale via useTheme() / useLocale().

TypeScript (core props)

interface MarkdownRendererProps {
  source: string;
  options?: ProcessorOptions;
  className?: string;
  theme?: 'light' | 'dark' | 'auto';
  showToc?: boolean;
  tocPosition?: 'left' | 'right';
  debounceMs?: number;
  onRendered?: (info: { html: string; toc: TocItem[] }) => void;
  onLinkClick?: (href: string, event: React.MouseEvent) => void;
  onImageClick?: (src: string, alt: string, event: React.MouseEvent) => void;
  components?: Partial<Record<string, React.ComponentType<any>>>;
  streaming?: boolean;
  onStreamEnd?: () => void;
  height?: string;
  minHeight?: string;
  maxHeight?: string;
}

ProcessorOptions

Option Type Default Description
gfm boolean true GitHub Flavored Markdown
math boolean true KaTeX
mermaid boolean true Mermaid code blocks
frontmatter boolean true YAML/TOML front matter
emoji boolean true Emoji shortcodes
toc boolean false [[toc]] / [toc] replacement
sanitize boolean true HTML sanitization
sanitizeSchema Schema internal Custom rehype-sanitize schema
codeTheme string 'github-dark' Shiki theme
highlight boolean true Shiki highlighting (async pipeline)
allowHtml boolean true Raw HTML path through remark-rehype
remarkPlugins Plugin[] [] Extra remark plugins
rehypePlugins Plugin[] [] Extra rehype plugins

Exported Utilities

Export Description
createProcessor, renderMarkdown, renderMarkdownSync, parseToAst Core unified pipeline
ProcessorOptions Pipeline configuration type
rehypeHighlightCode Shiki highlighting rehype plugin
renderMermaidDiagram, initMermaid Client Mermaid helpers
extractToc, remarkToc, TocItem TOC extraction / remark plugin
remarkAlert, remarkContainer, remarkCodeMeta Alert, container, code-meta remark plugins
parseCodeMeta, extractCodeBlocks, CodeBlockMeta Fence meta parsing
sanitizeUrl, processExternalLinks, escapeHtml Security helpers
rehypeA11y Accessibility rehype plugin
MarkdownWorkerRenderer, RenderCache, splitHtmlBlocks Worker / cache utilities
copyToClipboard Clipboard helper
slug, resetSlugger Heading slug utilities
setLocale, getLocale, t, getMessages i18n API

Hooks

Hook Description
useMarkdown(source, options?) Returns { html, toc, isLoading, error, refresh }
useDebouncedValue(value, delay) Debounced value
useScrollSync(editorRef, previewRef) Bi-directional scroll sync

Component Architecture

┌─────────────────────────────────────────────────────────────┐
│                    MarkdownProvider                            │
│              (theme / locale context)                        │
└───────────────────────────┬─────────────────────────────────┘
                            │
        ┌───────────────────┼───────────────────┐
        ▼                   ▼                   ▼
 ┌──────────────┐  ┌──────────────┐  ┌──────────────────┐
 │ MarkdownEditor│  │MarkdownRenderer│ │ MarkdownViewer  │
 │ ┌──────────┐ │  │ • unified +    │  │ • useMarkdown    │
 │ │ CodeMirror│ │  │   Shiki/KaTeX  │  │ • no CM dep      │
 │ │ + Toolbar │ │  │ • TOC sidebar  │  └──────────────────┘
 │ └──────────┘ │  │ • Mermaid/SVG  │
 │ ┌──────────┐ │  │ • code actions │
 │ │ Preview  │◄┼──┤   (copy/…)     │
 │ │ (Renderer)│ │  └────────────────┘
 │ └──────────┘ │
 └──────────────┘

Sub-Projects

Path Description
website/ Vite dev playground / demo app for local development
src/styles/ Base markdown-renderer.css and theme presets (themes/notion.css, themes/typora.css)

Customization

Extra theme presets

import '@xcan-cloud/markdown/styles';
// Optional presets:
import '@xcan-cloud/markdown/themes/notion.css';
import '@xcan-cloud/markdown/themes/typora.css';

CSS variables

Override tokens on .markdown-renderer (see stylesheet for --md-* variables).

i18n

<MarkdownProvider defaultLocale="zh-CN">
  <MarkdownEditor initialValue="# 你好" />
</MarkdownProvider>
import { setLocale, t } from '@xcan-cloud/markdown';

setLocale('zh-CN');

Toolbar

<MarkdownEditor toolbar={false} />
<MarkdownEditor toolbar={['bold', 'italic', '|', 'code']} />

Height

// Fixed height
<MarkdownRenderer source={md} height="600px" />
<MarkdownViewer source={md} height="400px" />

// Min / max height
<MarkdownRenderer source={md} minHeight="200px" maxHeight="80vh" />

// Editor CodeMirror pane height
<MarkdownEditor minHeight="300px" maxHeight="700px" />

Code fence meta

```python filename=hello.py
print("hi")
```

Use parseCodeMeta / extractCodeBlocks for external tooling.

Technology Stack

Category Technologies
Framework React 18+, TypeScript
Markdown unified, remark, rehype, remark-gfm, remark-math, …
Highlighting Shiki
Diagrams Mermaid (client), KaTeX
Editor CodeMirror 6
Icons lucide-react
Build Vite, vite-plugin-dts

Browser Support

Modern evergreen browsers (Chrome, Firefox, Safari, Edge — last 2 major versions). Features like fetch streams / Workers follow browser capabilities.

Development

npm install
npm run dev      # website demo
npm run build    # library dist
npm test
npm run lint     # tsc --noEmit

Contributing

  1. Fork the repository.
  2. Create a branch: git checkout -b feat/your-feature.
  3. Commit with clear messages.
  4. Push and open a Pull Request.

Please ensure npm run lint and npm run build pass before submitting.

License

MIT © Markdown package contributors

About

Production-grade, extensible, high-performance Markdown rendering & editing component for React.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors