Obsidian-style Live Preview for CodeMirror 6 โ A modular plugin collection that hides Markdown syntax when you're not editing it.
English | ็ฎไฝไธญๆ
Live Demo ยท Documentation ยท Roadmap
Most Markdown editors force you to choose: either see raw syntax or rendered output. Live Preview gives you both โ syntax fades in only when your cursor enters, letting you edit naturally while seeing formatted results.
Key differentiators:
- Modular โ Import only what you need (math? tables? code blocks?)
- Zero lock-in โ Works with any CodeMirror 6 setup
- Lightweight โ No heavy dependencies forced on you
| Feature | Description | Since |
|---|---|---|
| โจ Live Preview | Hide markers when not editing | v0.1.0 |
| ๐ Inline Formatting | Bold, italic, strikethrough, inline code | v0.1.0 |
| ๐ Block Elements | Headers, lists, blockquotes | v0.1.0 |
| ๐งฎ Math | KaTeX rendering (inline & block) | v0.2.0 |
| ๐ Tables | GFM table rendering | v0.3.0 |
| ๐ป Code Blocks | Syntax highlighting via lowlight | v0.4.0 |
| ๐ผ๏ธ Images | Image preview with loading states | v0.5.0 |
| ๐ Links | Clickable link rendering | v0.5.0 |
| ๐งฉ Editable Tables (Advanced) | Optional table editor with inline cells + source toggle | v0.5.1 |
npm install codemirror-live-markdownPeer dependencies:
npm install @codemirror/state @codemirror/view @codemirror/lang-markdown @codemirror/language @lezer/markdownOptional dependencies (install only what you need):
npm install katex # For math formulas
npm install lowlight # For code syntax highlightingimport { EditorState } from '@codemirror/state';
import { EditorView } from '@codemirror/view';
import { markdown } from '@codemirror/lang-markdown';
import {
livePreviewPlugin,
markdownStylePlugin,
editorTheme,
mouseSelectingField,
collapseOnSelectionFacet,
setMouseSelecting,
} from 'codemirror-live-markdown';
const view = new EditorView({
state: EditorState.create({
doc: '# Hello World\n\nThis is **bold** and *italic* text.',
extensions: [
markdown(),
collapseOnSelectionFacet.of(true),
mouseSelectingField,
livePreviewPlugin,
markdownStylePlugin,
editorTheme,
],
}),
parent: document.getElementById('editor')!,
});
// Required: Track mouse selection state
view.contentDOM.addEventListener('mousedown', () => {
view.dispatch({ effects: setMouseSelecting.of(true) });
});
document.addEventListener('mouseup', () => {
requestAnimationFrame(() => {
view.dispatch({ effects: setMouseSelecting.of(false) });
});
});Each feature is a separate plugin. Import and add only what you need:
import { Table } from '@lezer/markdown';
import {
mathPlugin,
blockMathField,
tableField,
tableEditorPlugin,
codeBlockField,
codeBlockEditorPlugin,
imageField,
linkPlugin,
} from 'codemirror-live-markdown';
const extensions = [
markdown({ extensions: [Table] }), // Enable GFM tables in parser
// ... core extensions from Quick Start
// Optional features:
mathPlugin, // Inline math: `$E=mc^2$`
blockMathField, // Block math: ```math
tableField, // GFM tables
tableEditorPlugin(), // Editable tables with source toggle
codeBlockField({ copyButton: true }), // Code blocks with syntax highlighting
codeBlockEditorPlugin({ copyButton: true }), // Code blocks with MD/Code toggle
imageField(), // Image preview
linkPlugin(), // Link rendering
];codeBlockField({
lineNumbers: false, // Show line numbers
copyButton: true, // Show copy button
defaultLanguage: 'text', // Fallback language
interaction: 'auto', // 'auto' | 'toggle'
})interaction: 'auto'(default): enter source mode when cursor enters code block.interaction: 'toggle': keep rendered mode by default and switch withMD/Codebuttons.
codeBlockEditorPlugin({
copyButton: true,
lineNumbers: false,
defaultLanguage: 'text',
})codeBlockEditorPlugin() is a non-breaking convenience API for codeBlockField({ interaction: 'toggle' }).
import { registerLanguage, initHighlighter } from 'codemirror-live-markdown';
import rust from 'highlight.js/lib/languages/rust';
// Initialize highlighter (required before first use)
await initHighlighter();
// Register additional languages
registerLanguage('rust', rust);Customize with CSS variables:
:root {
--md-heading: #1a1a1a;
--md-bold: #1a1a1a;
--md-italic: #1a1a1a;
--md-link: #2563eb;
--md-code-bg: #f5f5f5;
}| Export | Description |
|---|---|
livePreviewPlugin |
Main live preview behavior |
markdownStylePlugin |
Styling for headers, bold, italic, etc. |
editorTheme |
Default theme with animations |
mouseSelectingField |
Tracks drag selection state |
collapseOnSelectionFacet |
Enable/disable live preview |
| Export | Description | Requires |
|---|---|---|
mathPlugin |
Inline math rendering | katex |
blockMathField |
Block math rendering | katex |
tableField |
Table rendering | @lezer/markdown Table |
tableEditorPlugin() |
Editable table rendering | @lezer/markdown Table |
codeBlockField(options?) |
Code block highlighting | lowlight |
codeBlockEditorPlugin(options?) |
Code block editable preview with MD/Code toggle | lowlight |
setCodeBlockSourceMode |
Toggle code block source mode via effect | โ |
imageField(options?) |
Image preview | โ |
linkPlugin(options?) |
Link rendering | โ |
| Export | Description |
|---|---|
shouldShowSource(state, from, to) |
Check if range should show source |
renderMath(source, displayMode) |
Render LaTeX to HTML |
highlightCode(code, lang?) |
Highlight code string |
initHighlighter() |
Initialize syntax highlighter |
isHighlighterAvailable() |
Check if highlighter is ready |
git clone https://github.com/blueberrycongee/codemirror-live-markdown.git
cd codemirror-live-markdown
npm install
npm run dev # Watch mode
npm test # Run tests
npm run build # Production buildRun the demo:
cd demo
npm install
npm run devContributions are welcome! Please read our contributing guidelines before submitting a PR.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Inspired by Obsidian's Live Preview mode
- Built on CodeMirror 6
- Syntax highlighting powered by lowlight
- Math rendering by KaTeX