A modern, extensible markdown editor and previewer for the web.
Installation • Quick Start • Usage • Features • API • License
Draftly is a powerful, pluggable markdown editor and preview toolkit built on top of CodeMirror 6. It provides a seamless "rich text" editing experience while preserving standard markdown syntax. Draftly also includes a static HTML renderer that produces output visually identical to the editor, making it perfect for blogs, documentation sites, and content management systems.
- 🚀 Modern Architecture: Built on CodeMirror 6 with incremental Lezer parsing.
- 🎨 Rich Editing: WYSIWYG-like experience with full markdown control.
- 🔌 Extensible Plugin System: Add custom rendering, keymaps, and syntax.
- 🖼️ Static Preview: Render markdown to semantic HTML with visual parity.
- 🌗 Theming: First-class support for light and dark modes.
- 📦 Modular Exports: Import only what you need (
draftly/editor,draftly/preview,draftly/plugins).
Install the package via your preferred package manager:
# npm
npm install draftly
# yarn
yarn add draftly
# pnpm
pnpm add draftly
# bun
bun add draftlyDraftly requires the following CodeMirror packages as peer dependencies. Make sure they are installed in your project:
npm install @codemirror/commands @codemirror/lang-markdown @codemirror/language @codemirror/language-data @codemirror/state @codemirror/viewGet up and running in seconds.
import{EditorView}from"@codemirror/view";import{EditorState}from"@codemirror/state";import{draftly}from"draftly";constview=newEditorView({state: EditorState.create({doc: "# Hello, Draftly!",extensions: [draftly()],}),parent: document.getElementById("editor")!,});Draftly is designed for flexibility. Use it as a CodeMirror extension for interactive editing or as a standalone renderer for static previews.
Here's a complete example using @uiw/react-codemirror:
importCodeMirrorfrom"@uiw/react-codemirror";import{draftly,allPlugins,ThemeEnum}from"draftly";import{githubDark}from"@uiw/codemirror-theme-github";functionMarkdownEditor(){return(<CodeMirrorvalue="# Welcome to Draftly\n\nStart writing..."height="500px"extensions={[draftly({theme: ThemeEnum.DARK,themeStyle: githubDark,plugins: allPlugins,lineWrapping: true,history: true,indentWithTab: true,onNodesChange: (nodes)=>console.log("AST:",nodes),}),]}/>);}| Option | Type | Default | Description |
|---|---|---|---|
theme | ThemeEnum | ThemeEnum.AUTO | Theme mode: LIGHT, DARK, or AUTO. |
themeStyle | Extension | undefined | CodeMirror theme extension (e.g., githubDark). |
plugins | DraftlyPlugin[] | [] | Plugins to enable for rendering and parsing. |
baseStyles | boolean | true | Load default base styles. |
disableViewPlugin | boolean | false | Disable rich rendering (raw markdown mode). |
defaultKeybindings | boolean | true | Enable default CodeMirror keybindings. |
history | boolean | true | Enable undo/redo history. |
indentWithTab | boolean | true | Use Tab for indentation. |
highlightActiveLine | boolean | true | Highlight the current line (in raw mode). |
lineWrapping | boolean | true | Enable line wrapping. |
onNodesChange | (nodes: DraftlyNode[]) => void | undefined | Callback fired on every document update with parsed AST. |
markdown | MarkdownConfig[] | [] | Additional Lezer markdown parser extensions. |
extensions | Extension[] | [] | Additional CodeMirror extensions. |
keymap | KeyBinding[] | [] | Additional keybindings. |
Render markdown to semantic HTML for server-side rendering, static site generation, or read-only views.
import{preview,generateCSS,allPlugins,ThemeEnum}from"draftly";constmarkdown=`# Hello WorldThis is a **bold** statement with some \`inline code\`.- Item 1- Item 2- Item 3`;// Generate HTMLconsthtml=preview(markdown,{theme: ThemeEnum.LIGHT,plugins: allPlugins,sanitize: true,wrapperClass: "prose",});// Generate matching CSSconstcss=generateCSS({theme: ThemeEnum.LIGHT,plugins: allPlugins,wrapperClass: "prose",includeBase: true,});// Use in your appfunctionArticlePreview(){return(<><style>{css}</style><articledangerouslySetInnerHTML={{__html: html}}/></>);}| Option | Type | Default | Description |
|---|---|---|---|
plugins | DraftlyPlugin[] | [] | Plugins for rendering. |
theme | ThemeEnum | ThemeEnum.AUTO | Theme mode. |
sanitize | boolean | true | Sanitize HTML output (via DOMPurify). |
wrapperClass | string | "draftly-preview" | CSS class for the wrapper element. |
wrapperTag | string | "article" | HTML tag for the wrapper element. |
markdown | MarkdownConfig[] | [] | Additional parser extensions. |
Draftly's ViewPlugin decorates the editor to hide markdown syntax and render styled content inline. This provides a WYSIWYG-like experience while keeping the source as plain markdown.
- Inline Formatting: Bold, italic, strikethrough, and code are styled in-place.
- Headings: Rendered with proper sizes and weights.
- Lists: Ordered and unordered lists with custom bullets.
- Images: Displayed inline with alt text and captions.
- Links: Clickable with visual distinction.
- Code Blocks: Syntax highlighted with language detection.
Every feature in Draftly is a plugin. Plugins can provide:
- CodeMirror Extensions: Custom decorations, widgets, and behaviors.
- Markdown Parser Extensions: Extend the Lezer parser for custom syntax.
- Keymaps: Add keyboard shortcuts.
- Themes: Inject custom styles based on the current theme.
- Preview Renderers: Define how elements are rendered to static HTML.
import{DraftlyPlugin}from"draftly/editor";classMyCustomPluginextendsDraftlyPlugin{name="my-custom-plugin";onRegister(context){console.log("Plugin registered!",context.config);}getExtensions(){return[/* CodeMirror extensions */];}getKeymap(){return[/* KeyBinding[] */];}getMarkdownConfig(){return{/* MarkdownConfig */};}theme(mode){return{/* Theme spec */};}}Access the parsed document structure via the onNodesChange callback. Perfect for building:
- Table of Contents
- Document Outlines
- Navigation Breadcrumbs
- Word/Line Counters
typeDraftlyNode={from: number;// Start positionto: number;// End positionname: string;// Node type (e.g., "Heading", "Paragraph")children: DraftlyNode[];isSelected: boolean;// True if cursor is within this node};Draftly provides seamless theming with automatic light/dark mode support:
- Auto Detection: Follows system preference with
ThemeEnum.AUTO. - Manual Control: Force
ThemeEnum.LIGHTorThemeEnum.DARK. - Custom Themes: Pass any CodeMirror theme via
themeStyle. - Preview Parity: CSS generation ensures preview matches editor styling.
Import only what you need to minimize bundle size:
// Full packageimport{draftly,preview,allPlugins}from"draftly";// Editor onlyimport{draftly,DraftlyPlugin}from"draftly/editor";// Preview onlyimport{preview,generateCSS}from"draftly/preview";// Individual pluginsimport{HeadingPlugin,ListPlugin}from"draftly/plugins";| Export | Path | Description |
|---|---|---|
draftly | draftly/editor | Main editor extension factory. |
DraftlyPlugin | draftly/editor | Base class for creating plugins. |
ThemeEnum | draftly/editor | Enum for theme modes (AUTO, LIGHT, DARK). |
DraftlyNode | draftly/editor | Type for AST nodes. |
preview | draftly/preview | Function to render markdown to HTML. |
generateCSS | draftly/preview | Function to generate CSS for preview styling. |
allPlugins | draftly/plugins | Array of all built-in plugins. |
Draftly supports all modern browsers:
| Browser | Version |
|---|---|
| Chrome | 88+ |
| Firefox | 78+ |
| Safari | 14+ |
| Edge | 88+ |
Contributions are welcome! Please read our Contributing Guide before submitting a pull request.