@michaelcuneo/markdown-editor
v0.0.31
Published
A Svelte-based Markdown editor component with ProseMirror and CodeMirror integration.
Readme
@michaelcuneo/markdown-editor
A modern WYSIWYM Markdown editor for Svelte 5, built with ProseMirror and CodeMirror 6.
It supports live Markdown editing, task lists, fenced code blocks with syntax highlighting, keyboard shortcuts, and a fully themeable light/dark design system powered by CSS variables.
Live Demo
Demo: https://markdown-editor.michaelcuneo.com.au
Try headings, inline formatting, code fences, lists, and interactive task items rendered live as you type.
Built with Svelte 5, TypeScript, ProseMirror, and CodeMirror 6.
Features
- WYSIWYM Markdown editing
- Fast WYSIWYG ↔ Markdown source switching
- Two-way binding with
bind:markdown - Task lists with interactive checkboxes
- Image manipulation tools (replace, remove, caption, drag, resize)
- Optional responsive image optimization controls
- Fenced code blocks with CodeMirror 6
- Syntax highlighting
- Keyboard shortcuts
- Read-only and runtime editable modes
- Per-document reset support with
docId - Fully themeable via CSS custom properties
- Optional HTML sanitization with DOMPurify
- Works in SSR and SPA environments
Install
Install the package plus its peer dependencies:
npm install @michaelcuneo/markdown-editor \
prosemirror-state \
prosemirror-view \
prosemirror-model \
prosemirror-commands \
prosemirror-markdown \
prosemirror-history \
prosemirror-keymap \
prosemirror-inputrules \
prosemirror-schema-list \
codemirror \
@codemirror/state \
@codemirror/view \
@codemirror/language \
@codemirror/theme-one-dark \
@codemirror/lang-javascript \
@codemirror/lang-markdown \
@codemirror/lang-pythonIf you use pnpm:
pnpm add @michaelcuneo/markdown-editor \
prosemirror-state \
prosemirror-view \
prosemirror-model \
prosemirror-commands \
prosemirror-markdown \
prosemirror-history \
prosemirror-keymap \
prosemirror-inputrules \
prosemirror-schema-list \
codemirror \
@codemirror/state \
@codemirror/view \
@codemirror/language \
@codemirror/theme-one-dark \
@codemirror/lang-javascript \
@codemirror/lang-markdown \
@codemirror/lang-pythonThese are peer dependencies so your app can control versions and avoid conflicts.
Usage
<script lang="ts">
import { SvelteMarkdownEditor } from '@michaelcuneo/markdown-editor';
import '@michaelcuneo/markdown-editor/styles.css';
let content = `# Welcome
This is a **Markdown editor** built with Svelte 5.
- [x] Task lists
- [ ] Live preview
- [ ] Syntax highlighting
\`\`\`ts
function greet(name: string): string {
return \`Hello, \${name}!\`;
}
\`\`\`
`;
</script>
<SvelteMarkdownEditor bind:markdown={content} />Two-Way Binding
The editor is reactive by design.
<script lang="ts">
import { SvelteMarkdownEditor } from '@michaelcuneo/markdown-editor';
let markdown = '# Hello';
</script>
<SvelteMarkdownEditor bind:markdown />
<p>Characters: {markdown.length}</p>Props
| Prop | Type | Bindable | Default | Description |
| -------------- | ------------------------------- | -------- | ----------- | ------------------------------------------------------------------------------------------------------------------ |
| markdown | string | ✅ | '' | The markdown content. Updates as the user types. |
| toolbar | boolean | | true | Whether to render the toolbar above the editor. |
| imageQueue | Record<string, MarkdownImage> | ✅ | {} | Image queue map, updated as images are added, optimised, and uploaded. |
| clearDraft | boolean | ✅ | false | Set to true to clear the auto-saved draft. Resets to false after. |
| docId | string | | 'default' | Namespaces the auto-saved draft in localStorage. Change per document. |
| editable | boolean | | true | Whether the editor content is editable. |
| bare | boolean | | false | Removes all border, background, and toolbar for display-only/readonly markdown. Best used with editable={false}. |
| allowHtml | boolean | | false | Allow raw HTML passthrough in WYSIWYG mode. |
| viewMode | 'wysiwyg' \| 'markdown' | ✅ | 'wysiwyg' | Switches between rich-text and raw markdown source mode. |
| imageOptions | MarkdownImageOptions | | {} | Image handling options — storage strategy, optimization, upload callback. |
View modes
Markdownmode shows a raw markdown source textarea for direct editing.
imageOptions (optional)
The editor stays neutral by default: images are added to imageQueue and previewed locally.
This README focuses on built-in image manipulation + optimization behavior.
| Key | Type | Default | Description |
| -------------------- | ------------------------------------ | ------------------------------------------ | -------------------------------------------------------------------- |
| enableOptimization | boolean | false | Enables Sharpless-based optimization controls in image UI. |
| optimizeOnDrop | boolean | true | If optimization is enabled, auto-optimizes dropped/pasted images. |
| quality | number | 0.82 | Default optimization quality target. |
| formats | string[] | ['image/webp','image/jpeg','image/avif'] | Output formats requested from optimizer. |
| targets | { width: number; label: string }[] | Built-in responsive targets | Output widths/labels for responsive variants and generated srcSet. |
| preferredFormat | string | 'image/webp' | Preferred format for preview + responsive source selection. |
Image manipulation behavior
- Inline caption editing (
alttext) - Replace image from local file picker
- Remove image node from document
- Drag-to-reorder image blocks
- Corner resize handles with persisted dimensions
- Optional optimization panel (
quality,widths,format) with Apply action
Example: keep everything local (no uploads)
<script lang="ts">
import { SvelteMarkdownEditor } from '@michaelcuneo/markdown-editor';
let markdown = '# Hello';
let imageQueue = {};
</script>
<SvelteMarkdownEditor
bind:markdown
{imageQueue}
imageOptions={{
enableOptimization: true,
storage: 'local',
optimizeOnDrop: false
}}
/>Save contract (consumer-owned)
On Save, host applications decide what to do with imageQueue and generated variants.
Typical flows include persisting markdown as-is, replacing refs, uploading files, or discarding transient queue state.
Examples
Reset on document change
<script lang="ts">
import { SvelteMarkdownEditor } from '@michaelcuneo/markdown-editor';
let docId = 'intro';
let markdown = '# Welcome';
function loadNewDoc() {
docId = 'notes';
markdown = '## Empty new document';
}
</script>
<button onclick={loadNewDoc}>New Document</button>
<SvelteMarkdownEditor bind:markdown {docId} />Display-only/readonly Markdown (bare mode)
<script lang="ts">
import { SvelteMarkdownEditor } from '@michaelcuneo/markdown-editor';
let markdown = '# Hello';
</script>
<SvelteMarkdownEditor bind:markdown {markdown} editable={false} bare={true} />This renders the markdown with no border, background, or toolbar—ideal for embedding read-only markdown content in your app.
Start in Markdown source mode
<script lang="ts">
import { SvelteMarkdownEditor } from '@michaelcuneo/markdown-editor';
let markdown = '# Hello';
let viewMode: 'wysiwyg' | 'markdown' = 'markdown';
</script>
<SvelteMarkdownEditor bind:markdown bind:viewMode />Toolbar & Shortcuts
| Action | Shortcut | | ----------- | ------------------------ | | Bold | Ctrl/Cmd + B | | Italic | Ctrl/Cmd + I | | Headings | Toolbar or # syntax | | Blockquote | > then space | | Lists | -, *, 1. then space | | Task item | - [ ] or - [x] | | Code block | ``` + language + Enter | | Link | Ctrl/Cmd + K | | View mode | Ctrl/Cmd + Shift + M | | Undo / Redo | Ctrl/Cmd + Z / Shift + Z |
Markdown Support
- Headings
- Bold, italic, strikethrough
- Inline code and fenced code blocks
- Blockquotes
- Lists and task lists
- Tables (GFM)
- Links
- Horizontal rules
- Syntax-highlighted code blocks
Styling & Theming
Uses CSS custom properties under the --md-* namespace.
Global theme
:root {
--md-bg: #fdfdfd;
--md-fg: #222;
--md-accent: #0b57d0;
--md-code-bg: #f9fafb;
--md-code-fg: #0056b3;
}Per-instance theme
<div class="markdown-theme-ocean">
<SvelteMarkdownEditor bind:markdown />
</div>.markdown-theme-ocean {
--md-bg: #001a26;
--md-fg: #cde9ff;
--md-accent: #00b7ff;
--md-code-bg: #011e2a;
--md-selection-bg: #00384d;
}Styles
import '@michaelcuneo/markdown-editor/styles.css';CodeMirror Integration
- Uses One Dark-style theme in dark mode
- GitHub-style palette in light mode
- Fully overrideable via your own theme configuration
Architecture
| Layer | Role | | -------------- | ----------------------------------------- | | ProseMirror | Document model, schema, commands, history | | CodeMirror 6 | Fenced code block editor | | Custom plugins | Task lists, sync, enhancements | | DOMPurify | HTML sanitization | | Svelte 5 | Reactive component layer |
Compatibility
| Feature | Status | | ----------------- | ------ | | Svelte 5 | ✅ | | TypeScript | ✅ | | SSR + SPA | ✅ | | Light/Dark themes | ✅ | | CSS variables | ✅ | | Task lists | ✅ | | CodeMirror blocks | ✅ | | One Dark support | ✅ | | docId / editable | ✅ |
License
MIT © Michael Cuneo
