WYSIWYG HTML Editing
Traditional contentEditable + execCommand approach vs modern editor architectures.
Raw WYSIWYG Demo
This editor uses the browser's built-in contentEditable and document.execCommand() — the oldest approach to rich text editing on the web.
Try the toolbar buttons above. Notice how:
- Bold, italic, underline work as expected
- The HTML output can be messy (nested spans, empty tags)
- Undo/redo uses the browser's native stack
- No schema validation — any HTML structure is allowed
This is how TinyMCE and early CKEditor worked. Modern editors (ProseMirror, Tiptap, Lexical) use contentEditable as a rendering layer but manage state independently.
Click the HTML button to see the raw HTML source and edit it directly.
How Traditional WYSIWYG Works
javascript
// The traditional WYSIWYG approach — document.execCommand
const editor = document.getElementById('editor');
editor.contentEditable = 'true';
// Format commands
document.execCommand('bold'); // Toggle bold
document.execCommand('italic'); // Toggle italic
document.execCommand('underline'); // Toggle underline
document.execCommand('insertUnorderedList'); // Create bullet list
document.execCommand('formatBlock', false, '<h2>'); // Heading
document.execCommand('createLink', false, 'https://example.com');
// ⚠️ execCommand is deprecated!
// It's still widely supported but:
// - No spec guarantee
// - Inconsistent across browsers
// - No undo/redo control
// - Produces messy HTML
// - No custom block typesModern Approach
Modern editors still use contentEditable for rendering, but they manage the document state independently:
javascript
// Modern approach: ProseMirror/Tiptap/Lexical
// Instead of execCommand, modern editors use:
// 1. Immutable state + transactions
const tr = state.tr.addMark(from, to, schema.marks.bold.create());
view.dispatch(tr);
// 2. Schema-validated documents
// Only allowed structures can exist
// 3. Custom rendering
// Full control over how each node renders
// 4. Proper undo/redo via history plugin
// Not browser's native undo stackThe Evolution
2000s
execCommand era — TinyMCE, CKEditor 3/4. Direct DOM manipulation via browser APIs. Messy HTML, inconsistent cross-browser.
2015
ProseMirror — First editor with immutable state, schema-defined documents, and functional transactions. Revolutionary architecture.
2016
Slate, Draft.js — React-based editors with controlled state. Pioneered component-based editing.
2020+
Tiptap, Lexical, CodeMirror 6 — Modern era. Headless architectures, CRDT collaboration, framework-agnostic (mostly).
Strengths (of raw contentEditable)
- Zero dependencies — built into every browser
- Simple to get started
- Free native undo/redo
- Spell check, autocomplete for free
Weaknesses
- execCommand is deprecated
- Produces messy, unpredictable HTML
- No document model or schema
- Inconsistent across browsers
- No collaboration support
- Cannot define custom block types